diff --git a/backend/Squidex.ruleset b/backend/Squidex.ruleset deleted file mode 100644 index e56b72abe..000000000 --- a/backend/Squidex.ruleset +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/backend/extensions/Squidex.Extensions/APM/ApplicationInsights/ApplicationInsightsPlugin.cs b/backend/extensions/Squidex.Extensions/APM/ApplicationInsights/ApplicationInsightsPlugin.cs index ad9bca6a2..4ee785de1 100644 --- a/backend/extensions/Squidex.Extensions/APM/ApplicationInsights/ApplicationInsightsPlugin.cs +++ b/backend/extensions/Squidex.Extensions/APM/ApplicationInsights/ApplicationInsightsPlugin.cs @@ -5,46 +5,27 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; -using Microsoft.ApplicationInsights; -using Microsoft.ApplicationInsights.DataContracts; -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; +using Azure.Monitor.OpenTelemetry.Exporter; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Squidex.Infrastructure.Plugins; -using Squidex.Log; namespace Squidex.Extensions.APM.ApplicationInsights { - public sealed class ApplicationInsightsPlugin : IPlugin, IStartupFilter + public sealed class ApplicationInsightsPlugin : IPlugin { - public Action Configure(Action next) - { - return builder => - { - var client = builder.ApplicationServices.GetRequiredService(); - - Profiler.SpanStarted += session => - { - session.Listen(client.StartOperation(session.Key)); - }; - - next(builder); - }; - } - public void ConfigureServices(IServiceCollection services, IConfiguration config) { - var isEnabled = config.GetValue("logging:applicationInsights"); - - if (isEnabled) + services.AddOpenTelemetryTracing(builder => { - services.AddSingleton(this); - services.AddApplicationInsightsTelemetry(); - services.AddSingleton(); - } + if (config.GetValue("logging:applicationInsights:enabled")) + { + builder.AddAzureMonitorTraceExporter(options => + { + config.GetSection("logging:applicationInsights").Bind(options); + }); + } + }); } } } diff --git a/backend/extensions/Squidex.Extensions/APM/ApplicationInsights/RoleNameTelemetryInitializer.cs b/backend/extensions/Squidex.Extensions/APM/ApplicationInsights/RoleNameTelemetryInitializer.cs deleted file mode 100644 index 22107f4cd..000000000 --- a/backend/extensions/Squidex.Extensions/APM/ApplicationInsights/RoleNameTelemetryInitializer.cs +++ /dev/null @@ -1,36 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Microsoft.ApplicationInsights.Channel; -using Microsoft.ApplicationInsights.Extensibility; -using Microsoft.Extensions.Configuration; - -namespace Squidex.Extensions.APM.ApplicationInsights -{ - public sealed class RoleNameTelemetryInitializer : ITelemetryInitializer - { - private readonly string roleName; - - public RoleNameTelemetryInitializer(IConfiguration configuration) - { - roleName = configuration.GetValue("logging:roleName"); - - if (string.IsNullOrWhiteSpace(roleName)) - { - roleName = "Squidex"; - } - } - - public void Initialize(ITelemetry telemetry) - { - if (string.IsNullOrEmpty(telemetry.Context.Cloud.RoleName)) - { - telemetry.Context.Cloud.RoleName = roleName; - } - } - } -} diff --git a/backend/extensions/Squidex.Extensions/APM/Datadog/DatadogPlugin.cs b/backend/extensions/Squidex.Extensions/APM/Datadog/DatadogPlugin.cs deleted file mode 100644 index d5fd577b1..000000000 --- a/backend/extensions/Squidex.Extensions/APM/Datadog/DatadogPlugin.cs +++ /dev/null @@ -1,82 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using Datadog.Trace; -using Datadog.Trace.Configuration; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Squidex.Infrastructure.Plugins; -using Squidex.Log; - -namespace Squidex.Extensions.APM.Datadog -{ - public sealed class DatadogPlugin : IPlugin, IStartupFilter - { - public Action Configure(Action next) - { - return builder => - { - builder.Use(async (context, next) => - { - using (var scope = Tracer.Instance.StartActive(context.Request.Path)) - { - try - { - scope.Span.SetTag("http.method", context.Request.Method); - - await next(); - } - catch (Exception ex) - { - scope.Span.SetException(ex); - throw; - } - finally - { - scope.Span.SetTag("http.status_code", context.Response.StatusCode.ToString()); - } - } - }); - - next(builder); - }; - } - - public void ConfigureServices(IServiceCollection services, IConfiguration config) - { - var isEnabled = config.GetValue("logging:datadog"); - - if (isEnabled) - { - services.AddSingleton(this); - - SetupTracer(); - SetupProfiler(); - } - } - - private static void SetupProfiler() - { - Profiler.SpanStarted += session => - { - session.Listen(Tracer.Instance.StartActive(session.Key)); - }; - } - - private static void SetupTracer() - { - var settings = TracerSettings.FromDefaultSources(); - - settings.ServiceName = "squidex"; - - Tracer.Instance = new Tracer(settings); - } - } -} diff --git a/backend/extensions/Squidex.Extensions/APM/Otlp/OtlpPlugin.cs b/backend/extensions/Squidex.Extensions/APM/Otlp/OtlpPlugin.cs new file mode 100644 index 000000000..caf4ae0ff --- /dev/null +++ b/backend/extensions/Squidex.Extensions/APM/Otlp/OtlpPlugin.cs @@ -0,0 +1,35 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Trace; +using Squidex.Infrastructure.Plugins; + +namespace Squidex.Extensions.APM.Datadog +{ + public sealed class OtlpPlugin : IPlugin + { + public void ConfigureServices(IServiceCollection services, IConfiguration config) + { + services.AddOpenTelemetryTracing(builder => + { + if (config.GetValue("logging:otlp:enabled")) + { + // See: https://docs.microsoft.com/aspnet/core/grpc/troubleshoot#call-insecure-grpc-services-with-net-core-client + AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); + + builder.AddOtlpExporter(options => + { + config.GetSection("logging:otlp").Bind(options); + }); + } + }); + } + } +} diff --git a/backend/extensions/Squidex.Extensions/APM/Stackdriver/StackdriverExceptionHandler.cs b/backend/extensions/Squidex.Extensions/APM/Stackdriver/StackdriverExceptionHandler.cs new file mode 100644 index 000000000..bec3acbc0 --- /dev/null +++ b/backend/extensions/Squidex.Extensions/APM/Stackdriver/StackdriverExceptionHandler.cs @@ -0,0 +1,39 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Google.Cloud.Diagnostics.AspNetCore; +using Microsoft.AspNetCore.Http; +using Squidex.Infrastructure; +using Squidex.Log; + +namespace Squidex.Extensions.APM.Stackdriver +{ + internal class StackdriverExceptionHandler : ILogAppender + { + private readonly DefaultHttpContext fallbackContext = new DefaultHttpContext(); + private readonly IExceptionLogger logger; + private readonly IHttpContextAccessor httpContextAccessor; + + public StackdriverExceptionHandler(IExceptionLogger logger, IHttpContextAccessor httpContextAccessor) + { + this.logger = logger; + + this.httpContextAccessor = httpContextAccessor; + } + + public void Append(IObjectWriter writer, SemanticLogLevel logLevel, Exception exception) + { + if (exception != null && exception is not DomainException) + { + var httpContext = httpContextAccessor.HttpContext; + + logger.Log(exception, httpContext ?? fallbackContext); + } + } + } +} diff --git a/backend/extensions/Squidex.Extensions/APM/Stackdriver/StackdriverPlugin.cs b/backend/extensions/Squidex.Extensions/APM/Stackdriver/StackdriverPlugin.cs new file mode 100644 index 000000000..c4bf69c28 --- /dev/null +++ b/backend/extensions/Squidex.Extensions/APM/Stackdriver/StackdriverPlugin.cs @@ -0,0 +1,37 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Trace; +using Squidex.Infrastructure.Plugins; +using Squidex.Log; + +namespace Squidex.Extensions.APM.Stackdriver +{ + public sealed class StackdriverPlugin : IPlugin + { + public void ConfigureServices(IServiceCollection services, IConfiguration config) + { + services.AddOpenTelemetryTracing(builder => + { + if (config.GetValue("logging:stackdriver:enabled")) + { + var projectId = config.GetValue("logging:stackdriver:projectId"); + + builder.UseStackdriverExporter(projectId); + + services.AddSingleton(); + + services.AddSingleton(); + } + }); + } + } +} diff --git a/backend/extensions/Squidex.Extensions/APM/Stackdriver/StackdriverSeverityLogAppender.cs b/backend/extensions/Squidex.Extensions/APM/Stackdriver/StackdriverSeverityLogAppender.cs new file mode 100644 index 000000000..b12567eca --- /dev/null +++ b/backend/extensions/Squidex.Extensions/APM/Stackdriver/StackdriverSeverityLogAppender.cs @@ -0,0 +1,43 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Squidex.Log; + +namespace Squidex.Extensions.APM.Stackdriver +{ + public sealed class StackdriverSeverityLogAppender : ILogAppender + { + public void Append(IObjectWriter writer, SemanticLogLevel logLevel, Exception exception) + { + var severity = GetSeverity(logLevel); + + writer.WriteProperty(nameof(severity), severity); + } + + private static string GetSeverity(SemanticLogLevel logLevel) + { + switch (logLevel) + { + case SemanticLogLevel.Trace: + return "DEBUG"; + case SemanticLogLevel.Debug: + return "DEBUG"; + case SemanticLogLevel.Information: + return "INFO"; + case SemanticLogLevel.Warning: + return "WARNING"; + case SemanticLogLevel.Error: + return "ERROR"; + case SemanticLogLevel.Fatal: + return "CRITICAL"; + default: + return "DEFAULT"; + } + } + } +} diff --git a/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj b/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj index 580759a0b..47a5e967b 100644 --- a/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj +++ b/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj @@ -13,16 +13,20 @@ - - - + + + + + + + diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj index 4f3e2bd98..b9ca82aab 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj @@ -20,7 +20,7 @@ - + diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs index 7c5f8a4bc..c58f3cbb5 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository.cs @@ -14,7 +14,6 @@ using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { @@ -46,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task> QueryAsync(DomainId appId, DomainId parentId, CancellationToken ct = default) { - using (Profiler.TraceMethod("QueryAsyncByQuery")) + using (Telemetry.Activities.StartMethod("QueryAsyncByQuery")) { var filter = BuildFilter(appId, parentId); @@ -61,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task> QueryChildIdsAsync(DomainId appId, DomainId parentId, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var filter = BuildFilter(appId, parentId); @@ -78,7 +77,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task FindAssetFolderAsync(DomainId appId, DomainId id, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var documentId = DomainId.Combine(appId, id); diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository_SnapshotStore.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository_SnapshotStore.cs index 3918da039..74e1286a7 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository_SnapshotStore.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetFolderRepository_SnapshotStore.cs @@ -17,7 +17,6 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.States; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { @@ -25,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { async Task<(AssetFolderDomainObject.State Value, bool Valid, long Version)> ISnapshotStore.ReadAsync(DomainId key) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var existing = await Collection.Find(x => x.DocumentId == key) @@ -42,7 +41,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets async Task ISnapshotStore.WriteAsync(DomainId key, AssetFolderDomainObject.State value, long oldVersion, long newVersion) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var entity = Map(value); @@ -52,7 +51,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets async Task ISnapshotStore.WriteManyAsync(IEnumerable<(DomainId Key, AssetFolderDomainObject.State Value, long Version)> snapshots) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var updates = snapshots.Select(Map).Select(x => new ReplaceOneModel( @@ -74,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets async Task ISnapshotStore.ReadAllAsync(Func callback, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { await Collection.Find(new BsonDocument(), Batching.Options).ForEachAsync(x => callback(Map(x), x.Version), ct); } @@ -82,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets async Task ISnapshotStore.RemoveAsync(DomainId key) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { await Collection.DeleteOneAsync(x => x.DocumentId == key); } diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs index 9f7f4ee2f..5031a2c71 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs @@ -18,7 +18,6 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb.Queries; using Squidex.Infrastructure.Translations; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { @@ -91,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task> QueryAsync(DomainId appId, DomainId? parentId, Q q, CancellationToken ct = default) { - using (Profiler.TraceMethod("QueryAsyncByQuery")) + using (Telemetry.Activities.StartMethod("QueryAsyncByQuery")) { try { @@ -153,7 +152,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task> QueryIdsAsync(DomainId appId, HashSet ids, CancellationToken ct = default) { - using (Profiler.TraceMethod("QueryAsyncByIds")) + using (Telemetry.Activities.StartMethod("QueryAsyncByIds")) { var assetEntities = await Collection.Find(BuildFilter(appId, ids)).Only(x => x.Id) @@ -168,7 +167,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task> QueryChildIdsAsync(DomainId appId, DomainId parentId, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var assetEntities = await Collection.Find(x => x.IndexedAppId == appId && !x.IsDeleted && x.ParentId == parentId).Only(x => x.Id) @@ -183,7 +182,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task FindAssetByHashAsync(DomainId appId, string hash, string fileName, long fileSize, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var assetEntity = await Collection.Find(x => x.IndexedAppId == appId && !x.IsDeleted && x.FileHash == hash && x.FileName == fileName && x.FileSize == fileSize) @@ -196,7 +195,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task FindAssetBySlugAsync(DomainId appId, string slug, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var assetEntity = await Collection.Find(x => x.IndexedAppId == appId && !x.IsDeleted && x.Slug == slug) @@ -209,7 +208,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task FindAssetAsync(DomainId appId, DomainId id, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var documentId = DomainId.Combine(appId, id); @@ -224,7 +223,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets public async Task FindAssetAsync(DomainId id, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var assetEntity = await Collection.Find(x => x.Id == id && !x.IsDeleted) diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs index ecbaecda2..198ffd8d8 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository_SnapshotStore.cs @@ -17,7 +17,6 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.States; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { @@ -25,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { async Task<(AssetDomainObject.State Value, bool Valid, long Version)> ISnapshotStore.ReadAsync(DomainId key) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var existing = await Collection.Find(x => x.DocumentId == key) @@ -42,7 +41,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets async Task ISnapshotStore.WriteAsync(DomainId key, AssetDomainObject.State value, long oldVersion, long newVersion) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var entity = Map(value); @@ -52,7 +51,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets async Task ISnapshotStore.WriteManyAsync(IEnumerable<(DomainId Key, AssetDomainObject.State Value, long Version)> snapshots) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var updates = snapshots.Select(Map).Select(x => new ReplaceOneModel( @@ -74,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets async Task ISnapshotStore.ReadAllAsync(Func callback, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { await Collection.Find(new BsonDocument(), Batching.Options).ForEachAsync(x => callback(Map(x), x.Version), ct); } @@ -82,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets async Task ISnapshotStore.RemoveAsync(DomainId key) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { await Collection.DeleteOneAsync(x => x.DocumentId == key); } diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs index 6844ed91b..ea61cec8d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs @@ -20,7 +20,6 @@ using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Queries; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { @@ -107,7 +106,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task> QueryAsync(IAppEntity app, List schemas, Q q, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { if (q.Ids != null && q.Ids.Count > 0) { @@ -136,7 +135,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Q q, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { if (q.Ids != null && q.Ids.Count > 0) { @@ -160,7 +159,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task FindContentAsync(ISchemaEntity schema, DomainId id, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await queryBdId.QueryAsync(schema, id, ct); } @@ -169,7 +168,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task QueryScheduledWithoutDataAsync(Instant now, Func callback, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { await queryScheduled.QueryAsync(now, callback, ct); } @@ -178,7 +177,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task> QueryIdsAsync(DomainId appId, HashSet ids, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await queryByIds.QueryIdsAsync(appId, ids, ct); } @@ -187,7 +186,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task> QueryIdsAsync(DomainId appId, DomainId schemaId, FilterNode filterNode, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await queryByQuery.QueryIdsAsync(appId, schemaId, filterNode, ct); } @@ -196,7 +195,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents public async Task HasReferrersAsync(DomainId appId, DomainId contentId, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await queryReferrers.CheckExistsAsync(appId, contentId, ct); } diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs index bfe46f45e..fbd153cdb 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs @@ -15,7 +15,6 @@ using Squidex.Domain.Apps.Entities.Contents.DomainObject; using Squidex.Infrastructure; using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.States; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { @@ -29,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents async Task<(ContentDomainObject.State Value, bool Valid, long Version)> ISnapshotStore.ReadAsync(DomainId key) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var version = await collectionAll.FindVersionAsync(key); @@ -39,7 +38,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents async Task ISnapshotStore.ClearAsync() { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { await collectionAll.ClearAsync(); await collectionPublished.ClearAsync(); @@ -48,7 +47,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents async Task ISnapshotStore.RemoveAsync(DomainId key) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { await collectionAll.RemoveAsync(key); await collectionPublished.RemoveAsync(key); @@ -57,7 +56,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents async Task ISnapshotStore.WriteAsync(DomainId key, ContentDomainObject.State value, long oldVersion, long newVersion) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { if (value.SchemaId.Id == DomainId.Empty) { @@ -72,7 +71,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents async Task ISnapshotStore.WriteManyAsync(IEnumerable<(DomainId Key, ContentDomainObject.State Value, long Version)> snapshots) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var entitiesPublished = new List(); var entitiesAll = new List(); diff --git a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj index cc50ada9c..ce81d17ca 100644 --- a/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj +++ b/backend/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj @@ -17,7 +17,7 @@ - + diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs index 995007747..0c383653b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs @@ -1,4 +1,4 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) @@ -19,7 +19,6 @@ using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Validation; -using Squidex.Log; using Squidex.Shared; using Squidex.Text; @@ -77,7 +76,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes public async Task> GetAppsAsync() { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var ids = await GetAppIdsAsync(); @@ -91,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes public async Task> GetAppsForUserAsync(string userId, PermissionSet permissions) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var ids = await Task.WhenAll( @@ -109,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes public async Task GetAppByNameAsync(string name, bool canCache = false) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { if (canCache) { @@ -132,7 +131,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes public async Task GetAppAsync(DomainId appId, bool canCache) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { if (canCache) { @@ -155,7 +154,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes private async Task> GetAppIdsByUserAsync(string userId) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await grainFactory.GetGrain(userId).GetIdsAsync(); } @@ -163,7 +162,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes private async Task> GetAppIdsAsync() { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await grainFactory.GetGrain(SingleGrain.Id).GetIdsAsync(); } @@ -171,7 +170,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes private async Task> GetAppIdsAsync(string[] names) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await grainFactory.GetGrain(SingleGrain.Id).GetIdsAsync(names); } @@ -179,7 +178,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Indexes private async Task GetAppIdAsync(string name) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await grainFactory.GetGrain(SingleGrain.Id).GetIdAsync(name); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetEnricher.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetEnricher.cs index 58a749372..edcffc1c1 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetEnricher.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetEnricher.cs @@ -14,7 +14,6 @@ using Squidex.Domain.Apps.Core.Tags; using Squidex.Infrastructure; using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Reflection; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.Assets.Queries { @@ -48,7 +47,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries Guard.NotNull(assets, nameof(assets)); Guard.NotNull(context, nameof(context)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var results = assets.Select(x => SimpleMapper.Map(x, new AssetEntity())).ToList(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetLoader.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetLoader.cs index 1fada21ec..95b03276a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetLoader.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetLoader.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Orleans; using Squidex.Domain.Apps.Entities.Assets.DomainObject; using Squidex.Infrastructure; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.Assets.Queries { @@ -24,7 +23,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries public async Task GetAsync(DomainId appId, DomainId id, long version) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var key = DomainId.Combine(appId, id); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryParser.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryParser.cs index d4496dcce..9715d1916 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryParser.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryParser.cs @@ -21,7 +21,6 @@ using Squidex.Infrastructure.Queries.Json; using Squidex.Infrastructure.Queries.OData; using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Validation; -using Squidex.Log; using Squidex.Text; namespace Squidex.Domain.Apps.Entities.Assets.Queries @@ -48,7 +47,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries Guard.NotNull(context, nameof(context)); Guard.NotNull(q, nameof(q)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var query = ParseClrQuery(q); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs index f4ed509e7..e88330785 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/AssetQueryService.cs @@ -12,7 +12,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.Options; using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Infrastructure; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.Assets.Queries { @@ -45,7 +44,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries public async Task> FindAssetFolderAsync(DomainId appId, DomainId id, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var result = new List(); @@ -71,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries public async Task> QueryAssetFoldersAsync(DomainId appId, DomainId parentId, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var assetFolders = await QueryFoldersCoreAsync(appId, parentId, ct); @@ -82,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries public async Task> QueryAssetFoldersAsync(Context context, DomainId parentId, CancellationToken ct = default) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var assetFolders = await QueryFoldersCoreAsync(context, parentId, ct); @@ -95,7 +94,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries { Guard.NotNull(context, nameof(context)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var asset = await FindByHashCoreAsync(context, hash, fileName, fileSize, ct); @@ -113,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries { Guard.NotNull(context, nameof(context)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var asset = await FindBySlugCoreAsync(context, slug, ct); @@ -131,7 +130,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries { Guard.NotNull(context, nameof(context)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var asset = await FindCoreAsync(id, ct); @@ -149,7 +148,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries { Guard.NotNull(context, nameof(context)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { IAssetEntity? asset; @@ -181,7 +180,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries return EmptyAssets; } - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { q = await queryParser.ParseAsync(context, q); @@ -215,7 +214,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Queries private async Task> TransformCoreAsync(Context context, IEnumerable assets, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await assetEnricher.EnrichAsync(assets, context, ct); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs index 7146a16f9..de2f2a81d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs @@ -136,7 +136,7 @@ namespace Squidex.Domain.Apps.Entities.Backup jobUrl: CurrentJob.Url.ToString() ); - using (Profiler.StartSession()) + using (Telemetry.Activities.StartActivity("RestoreBackup")) { try { @@ -156,14 +156,14 @@ namespace Squidex.Domain.Apps.Entities.Backup { await reader.CheckCompatibilityAsync(); - using (Profiler.Trace("ReadEvents")) + using (Telemetry.Activities.StartActivity("ReadEvents")) { await ReadEventsAsync(reader, handlers); } foreach (var handler in handlers) { - using (Profiler.TraceMethod(handler.GetType(), nameof(IBackupHandler.RestoreAsync))) + using (Telemetry.Activities.StartMethod(handler.GetType(), nameof(IBackupHandler.RestoreAsync))) { await handler.RestoreAsync(runningContext); } @@ -173,7 +173,7 @@ namespace Squidex.Domain.Apps.Entities.Backup foreach (var handler in handlers) { - using (Profiler.TraceMethod(handler.GetType(), nameof(IBackupHandler.CompleteRestoreAsync))) + using (Telemetry.Activities.StartMethod(handler.GetType(), nameof(IBackupHandler.CompleteRestoreAsync))) { await handler.CompleteRestoreAsync(runningContext); } @@ -194,8 +194,6 @@ namespace Squidex.Domain.Apps.Entities.Backup w.WriteProperty("status", "completed"); w.WriteProperty("operationId", ctx.jobId); w.WriteProperty("url", ctx.jobUrl); - - Profiler.Session?.Write(w); }); } catch (Exception ex) @@ -223,8 +221,6 @@ namespace Squidex.Domain.Apps.Entities.Backup w.WriteProperty("status", "failed"); w.WriteProperty("operationId", ctx.jobId); w.WriteProperty("url", ctx.jobUrl); - - Profiler.Session?.Write(w); }); } finally @@ -294,7 +290,7 @@ namespace Squidex.Domain.Apps.Entities.Backup private async Task DownloadAsync() { - using (Profiler.Trace("Download")) + using (Telemetry.Activities.StartActivity("Download")) { Log("Downloading Backup"); @@ -421,7 +417,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { var userMapping = new UserMapping(CurrentJob.Actor); - using (Profiler.Trace("CreateUsers")) + using (Telemetry.Activities.StartActivity("CreateUsers")) { Log("Creating Users"); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs index d97bd59f8..faa1c0fd5 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentEnricher.cs @@ -13,7 +13,6 @@ using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Reflection; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.Contents.Queries { @@ -51,7 +50,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries private async Task> EnrichInternalAsync(IEnumerable contents, bool cloneData, Context context, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var results = new List(); @@ -102,7 +101,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries { ct.ThrowIfCancellationRequested(); - using (Profiler.TraceMethod(step.ToString()!)) + using (Telemetry.Activities.StartMethod(step.ToString()!)) { await step.EnrichAsync(context, results, GetSchema, ct); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentLoader.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentLoader.cs index def19558c..33e23dcd9 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentLoader.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentLoader.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Orleans; using Squidex.Domain.Apps.Entities.Contents.DomainObject; using Squidex.Infrastructure; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.Contents.Queries { @@ -24,7 +23,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries public async Task GetAsync(DomainId appId, DomainId id, long version) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var key = DomainId.Combine(appId, id).ToString(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs index 212612ffe..105673a8e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs @@ -28,7 +28,6 @@ using Squidex.Infrastructure.Queries.Json; using Squidex.Infrastructure.Queries.OData; using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Validation; -using Squidex.Log; using Squidex.Text; namespace Squidex.Domain.Apps.Entities.Contents.Queries @@ -59,7 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries Guard.NotNull(context, nameof(context)); Guard.NotNull(q, nameof(q)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var query = await ParseClrQueryAsync(context, q, schema); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs index c430b46c5..e7cbe2c56 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryService.cs @@ -16,7 +16,6 @@ using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Translations; -using Squidex.Log; using Squidex.Shared; using Squidex.Shared.Identity; @@ -53,7 +52,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries { Guard.NotNull(context, nameof(context)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName); @@ -82,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries { Guard.NotNull(context, nameof(context)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { if (q == null) { @@ -114,7 +113,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries { Guard.NotNull(context, nameof(context)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { if (q == null) { @@ -160,7 +159,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries private async Task> TransformCoreAsync(Context context, IEnumerable contents, CancellationToken ct) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await contentEnricher.EnrichAsync(contents, context, ct); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesIndex.cs index 737322d39..abb6d0634 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Indexes/RulesIndex.cs @@ -13,7 +13,6 @@ using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.DomainObject; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.Rules.Indexes { @@ -33,7 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes public async Task> GetRulesAsync(DomainId appId) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var ids = await GetRuleIdsAsync(appId); @@ -47,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes private async Task> GetRuleIdsAsync(DomainId appId) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await Index(appId).GetIdsAsync(); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleEnricher.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleEnricher.cs index b4b1b2598..7dbdc5d7f 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleEnricher.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleEnricher.cs @@ -12,7 +12,6 @@ using Squidex.Domain.Apps.Entities.Rules.Repositories; using Squidex.Infrastructure; using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Reflection; -using Squidex.Log; namespace Squidex.Domain.Apps.Entities.Rules.Queries { @@ -42,7 +41,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Queries Guard.NotNull(rules, nameof(rules)); Guard.NotNull(context, nameof(context)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var results = new List(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs index f7d86dedb..53adf6b23 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs @@ -17,7 +17,6 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Validation; -using Squidex.Log; using Squidex.Text; namespace Squidex.Domain.Apps.Entities.Schemas.Indexes @@ -41,7 +40,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes public async Task> GetSchemasAsync(DomainId appId) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var ids = await GetSchemaIdsAsync(appId); @@ -55,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes public async Task GetSchemaByNameAsync(DomainId appId, string name, bool canCache) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var cacheKey = GetCacheKey(appId, name); @@ -80,7 +79,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes public async Task GetSchemaAsync(DomainId appId, DomainId id, bool canCache) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var cacheKey = GetCacheKey(appId, id); @@ -105,7 +104,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes private async Task GetSchemaIdAsync(DomainId appId, string name) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await Index(appId).GetIdAsync(name); } @@ -113,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes private async Task> GetSchemaIdsAsync(DomainId appId) { - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { return await Index(appId).GetIdsAsync(); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj index 1841176ed..199b24c30 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj +++ b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj @@ -18,7 +18,7 @@ - + all diff --git a/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj b/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj index bfd397200..ecf15442e 100644 --- a/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj +++ b/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj @@ -19,7 +19,7 @@ - + diff --git a/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj b/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj index 153ad5b31..49dac07fb 100644 --- a/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj +++ b/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj @@ -17,7 +17,7 @@ - + diff --git a/backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Reader.cs b/backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Reader.cs index 9143d59d5..f4a86c082 100644 --- a/backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Reader.cs +++ b/backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Reader.cs @@ -14,7 +14,6 @@ using System.Threading.Tasks; using MongoDB.Driver; using NodaTime; using Squidex.Infrastructure.MongoDb; -using Squidex.Log; using EventFilter = MongoDB.Driver.FilterDefinition; namespace Squidex.Infrastructure.EventSourcing @@ -48,7 +47,7 @@ namespace Squidex.Infrastructure.EventSourcing return EmptyEvents; } - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var commits = await Collection.Find( @@ -65,7 +64,7 @@ namespace Squidex.Infrastructure.EventSourcing { Guard.NotNullOrEmpty(streamName, nameof(streamName)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var commits = await Collection.Find( @@ -84,7 +83,7 @@ namespace Squidex.Infrastructure.EventSourcing { Guard.NotNull(streamNames, nameof(streamNames)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var position = EtagVersion.Empty; diff --git a/backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Writer.cs b/backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Writer.cs index bc4234e9e..eb58942b4 100644 --- a/backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Writer.cs +++ b/backend/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEventStore_Writer.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver; -using Squidex.Log; namespace Squidex.Infrastructure.EventSourcing { @@ -40,7 +39,7 @@ namespace Squidex.Infrastructure.EventSourcing Guard.LessThan(events.Count, MaxCommitSize, "events.Count"); Guard.GreaterEquals(expectedVersion, EtagVersion.Any, nameof(expectedVersion)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { if (events.Count == 0) { @@ -102,7 +101,7 @@ namespace Squidex.Infrastructure.EventSourcing { Guard.NotNull(commits, nameof(commits)); - using (Profiler.TraceMethod()) + using (Telemetry.Activities.StartMethod()) { var writes = new List>(); diff --git a/backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj b/backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj index ba8b59a5a..58c4a2330 100644 --- a/backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj +++ b/backend/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/backend/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs b/backend/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs index 616bacdaa..23cbe71bd 100644 --- a/backend/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs +++ b/backend/src/Squidex.Infrastructure.MongoDb/States/MongoSnapshotStore.cs @@ -14,7 +14,6 @@ using MongoDB.Bson; using MongoDB.Driver; using Newtonsoft.Json; using Squidex.Infrastructure.MongoDb; -using Squidex.Log; namespace Squidex.Infrastructure.States { @@ -45,7 +44,7 @@ namespace Squidex.Infrastructure.States public async Task<(T Value, bool Valid, long Version)> ReadAsync(DomainId key) { - using (Profiler.TraceMethod>()) + using (Telemetry.Activities.StartMethod>()) { var existing = await Collection.Find(x => x.DocumentId.Equals(key)) @@ -62,7 +61,7 @@ namespace Squidex.Infrastructure.States public async Task WriteAsync(DomainId key, T value, long oldVersion, long newVersion) { - using (Profiler.TraceMethod>()) + using (Telemetry.Activities.StartMethod>()) { await Collection.UpsertVersionedAsync(key, oldVersion, newVersion, u => u.Set(x => x.Doc, value)); } @@ -70,7 +69,7 @@ namespace Squidex.Infrastructure.States public Task WriteManyAsync(IEnumerable<(DomainId Key, T Value, long Version)> snapshots) { - using (Profiler.TraceMethod>()) + using (Telemetry.Activities.StartMethod>()) { var writes = snapshots.Select(x => new ReplaceOneModel>( Filter.Eq(y => y.DocumentId, x.Key), @@ -96,7 +95,7 @@ namespace Squidex.Infrastructure.States public async Task ReadAllAsync(Func callback, CancellationToken ct = default) { - using (Profiler.TraceMethod>()) + using (Telemetry.Activities.StartMethod>()) { await Collection.Find(new BsonDocument(), options: Batching.Options).ForEachAsync(x => callback(x.Doc, x.Version), ct); } @@ -104,7 +103,7 @@ namespace Squidex.Infrastructure.States public async Task RemoveAsync(DomainId key) { - using (Profiler.TraceMethod>()) + using (Telemetry.Activities.StartMethod>()) { await Collection.DeleteOneAsync(x => x.DocumentId.Equals(key)); } diff --git a/backend/src/Squidex.Infrastructure/Orleans/J{T}.cs b/backend/src/Squidex.Infrastructure/Orleans/J{T}.cs index 6e0d74791..2d13c1596 100644 --- a/backend/src/Squidex.Infrastructure/Orleans/J{T}.cs +++ b/backend/src/Squidex.Infrastructure/Orleans/J{T}.cs @@ -12,7 +12,6 @@ using Orleans.CodeGeneration; using Orleans.Concurrency; using Orleans.Serialization; using Squidex.Infrastructure.Json; -using Squidex.Log; #pragma warning disable IDE0060 // Remove unused parameter @@ -57,7 +56,7 @@ namespace Squidex.Infrastructure.Orleans [SerializerMethod] public static void Serialize(object? input, ISerializationContext context, Type? expected) { - using (Profiler.TraceMethod(nameof(J))) + using (Telemetry.Activities.StartMethod(nameof(J))) { var jsonSerializer = GetSerializer(context); @@ -70,7 +69,7 @@ namespace Squidex.Infrastructure.Orleans [DeserializerMethod] public static object? Deserialize(Type expected, IDeserializationContext context) { - using (Profiler.TraceMethod(nameof(J))) + using (Telemetry.Activities.StartMethod(nameof(J))) { var jsonSerializer = GetSerializer(context); diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index 9ea21eb73..8695875e0 100644 --- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -10,11 +10,11 @@ - + - - + + all @@ -23,12 +23,12 @@ - + - + diff --git a/backend/src/Squidex.Infrastructure/Telemetry.cs b/backend/src/Squidex.Infrastructure/Telemetry.cs new file mode 100644 index 000000000..59cd58489 --- /dev/null +++ b/backend/src/Squidex.Infrastructure/Telemetry.cs @@ -0,0 +1,33 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Squidex.Infrastructure +{ + public static class Telemetry + { + public static readonly ActivitySource Activities = new ActivitySource("Notifo"); + + public static Activity? StartMethod(this ActivitySource activity, Type type, [CallerMemberName] string? memberName = null) + { + return activity.StartActivity($"{type.Name}/{memberName}"); + } + + public static Activity? StartMethod(this ActivitySource activity, [CallerMemberName] string? memberName = null) + { + return activity.StartActivity($"{typeof(T).Name}/{memberName}"); + } + + public static Activity? StartMethod(this ActivitySource activity, string objectName, [CallerMemberName] string? memberName = null) + { + return activity.StartActivity($"{objectName}/{memberName}"); + } + } +} diff --git a/backend/src/Squidex.Web/ETagExtensions.cs b/backend/src/Squidex.Web/ETagExtensions.cs index 47e84866e..8df1a45aa 100644 --- a/backend/src/Squidex.Web/ETagExtensions.cs +++ b/backend/src/Squidex.Web/ETagExtensions.cs @@ -19,7 +19,7 @@ namespace Squidex.Web { public static string ToEtag(this IReadOnlyList items) where T : IEntity, IEntityWithVersion { - using (Profiler.Trace("CalculateEtag")) + using (Telemetry.Activities.StartActivity("CalculateEtag")) { var hash = Create(items, 0); @@ -29,7 +29,7 @@ namespace Squidex.Web public static string ToEtag(this IResultList entities) where T : IEntity, IEntityWithVersion { - using (Profiler.Trace("CalculateEtag")) + using (Telemetry.Activities.StartActivity("CalculateEtag")) { var hash = Create(entities, entities.Total); diff --git a/backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs b/backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs index 0730999ef..4b8796bb4 100644 --- a/backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs +++ b/backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Squidex.Domain.Apps.Entities.Apps.Plans; +using Squidex.Infrastructure; using Squidex.Log; namespace Squidex.Web.Pipeline @@ -48,7 +49,7 @@ namespace Squidex.Web.Pipeline { if (FilterDefinition.Costs > 0) { - using (Profiler.Trace("CheckUsage")) + using (Telemetry.Activities.StartActivity("CheckUsage")) { var (_, clientId) = context.HttpContext.User.GetClient(); diff --git a/backend/src/Squidex.Web/Pipeline/CachingManager.cs b/backend/src/Squidex.Web/Pipeline/CachingManager.cs index 35e71dbfe..a6d696d6e 100644 --- a/backend/src/Squidex.Web/Pipeline/CachingManager.cs +++ b/backend/src/Squidex.Web/Pipeline/CachingManager.cs @@ -99,7 +99,7 @@ namespace Squidex.Web.Pipeline { if (hasDependency && !response.Headers.ContainsKey(HeaderNames.ETag)) { - using (Profiler.Trace("CalculateEtag")) + using (Telemetry.Activities.StartActivity("CalculateEtag")) { var cacheBuffer = hasher.GetHashAndReset(); var cacheString = BitConverter.ToString(cacheBuffer).Replace("-", string.Empty).ToUpperInvariant(); diff --git a/backend/src/Squidex.Web/Pipeline/MeasureResultFilter.cs b/backend/src/Squidex.Web/Pipeline/MeasureResultFilter.cs index 81302f034..c1f1c6add 100644 --- a/backend/src/Squidex.Web/Pipeline/MeasureResultFilter.cs +++ b/backend/src/Squidex.Web/Pipeline/MeasureResultFilter.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Filters; -using Squidex.Log; +using Squidex.Infrastructure; namespace Squidex.Web.Pipeline { @@ -15,7 +15,7 @@ namespace Squidex.Web.Pipeline { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { - using (Profiler.Trace("ExecuteAction")) + using (Telemetry.Activities.StartActivity("ExecuteAction")) { await next(); } @@ -23,7 +23,7 @@ namespace Squidex.Web.Pipeline public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { - using (Profiler.Trace("ExecuteResult")) + using (Telemetry.Activities.StartActivity("ExecuteResult")) { await next(); } diff --git a/backend/src/Squidex.Web/Pipeline/RequestLogOptions.cs b/backend/src/Squidex.Web/Pipeline/RequestLogOptions.cs index 85ffadf04..e15eea34d 100644 --- a/backend/src/Squidex.Web/Pipeline/RequestLogOptions.cs +++ b/backend/src/Squidex.Web/Pipeline/RequestLogOptions.cs @@ -10,7 +10,5 @@ namespace Squidex.Web.Pipeline public sealed class RequestLogOptions { public bool LogRequests { get; set; } - - public bool LogProfiler { get; set; } } } diff --git a/backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs b/backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs index 20bf145c9..ac3757e6d 100644 --- a/backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs +++ b/backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs @@ -28,44 +28,23 @@ namespace Squidex.Web.Pipeline public async Task InvokeAsync(HttpContext context, ISemanticLog log) { - var shouldStartSession = requestLogOptions.LogRequests || Profiler.HasListener; - - if (requestLogOptions.LogRequests || shouldStartSession) + if (requestLogOptions.LogRequests) { - var session = - shouldStartSession ? - Profiler.StartSession() : - default; - - var watch = - requestLogOptions.LogRequests ? - ValueStopwatch.StartNew() : - default; + var watch = ValueStopwatch.StartNew(); - using (session) + try { - try - { - await next(context); - } - finally - { - if (requestLogOptions.LogRequests) - { - var elapsedMs = watch.Stop(); - - log.LogInformation((elapsedMs, context), (ctx, w) => - { - if (requestLogOptions.LogProfiler) - { - Profiler.Session?.Write(w); - } + await next(context); + } + finally + { + var elapsedMs = watch.Stop(); - w.WriteObject("filters", ctx.context, LogFilters); - w.WriteProperty("elapsedRequestMs", ctx.elapsedMs); - }); - } - } + log.LogInformation((elapsedMs, context), (ctx, w) => + { + w.WriteObject("filters", ctx.context, LogFilters); + w.WriteProperty("elapsedRequestMs", ctx.elapsedMs); + }); } } else @@ -74,33 +53,33 @@ namespace Squidex.Web.Pipeline } } - private static void LogFilters(HttpContext httpContext, IObjectWriter c) + private static void LogFilters(HttpContext httpContext, IObjectWriter obj) { var app = httpContext.Context().App; if (app != null) { - c.WriteProperty("appId", app.Id.ToString()); - c.WriteProperty("appName", app.Name); + obj.WriteProperty("appId", app.Id.ToString()); + obj.WriteProperty("appName", app.Name); } var userId = httpContext.User.OpenIdSubject(); if (!string.IsNullOrWhiteSpace(userId)) { - c.WriteProperty(nameof(userId), userId); + obj.WriteProperty(nameof(userId), userId); } var clientId = httpContext.User.OpenIdClientId(); if (!string.IsNullOrWhiteSpace(clientId)) { - c.WriteProperty(nameof(clientId), clientId); + obj.WriteProperty(nameof(clientId), clientId); } var costs = httpContext.Features.Get()?.Costs ?? 0; - c.WriteProperty(nameof(costs), costs); + obj.WriteProperty(nameof(costs), costs); } } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs index 843059eb5..f65ecbe08 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs @@ -18,12 +18,11 @@ using Squidex.Assets; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; -using Squidex.Domain.Apps.Entities.Apps.Plans; +using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Security; using Squidex.Infrastructure.Translations; using Squidex.Infrastructure.Validation; -using Squidex.Log; using Squidex.Shared; using Squidex.Web; @@ -267,25 +266,25 @@ namespace Squidex.Areas.Api.Controllers.Apps } catch (AssetNotFoundException) { - using (Profiler.Trace("Resize")) + using (Telemetry.Activities.StartActivity("Resize")) { using (var sourceStream = GetTempStream()) { using (var destinationStream = GetTempStream()) { - using (Profiler.Trace("ResizeDownload")) + using (Telemetry.Activities.StartActivity("ResizeDownload")) { await appImageStore.DownloadAsync(App.Id, sourceStream); sourceStream.Position = 0; } - using (Profiler.Trace("ResizeImage")) + using (Telemetry.Activities.StartActivity("ResizeImage")) { await assetThumbnailGenerator.CreateThumbnailAsync(sourceStream, destinationStream, ResizeOptions); destinationStream.Position = 0; } - using (Profiler.Trace("ResizeUpload")) + using (Telemetry.Activities.StartActivity("ResizeUpload")) { await assetStore.UploadAsync(resizedAsset, destinationStream); destinationStream.Position = 0; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs index 9a47b8b90..8f9c851e9 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs @@ -15,7 +15,6 @@ using Squidex.Areas.Api.Controllers.Plans; using Squidex.Areas.Api.Controllers.Rules; using Squidex.Areas.Api.Controllers.Schemas; using Squidex.Domain.Apps.Entities.Apps; -using Squidex.Domain.Apps.Entities.Apps.Plans; using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Reflection; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs index c0e1b143a..de881cbe9 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs @@ -203,19 +203,19 @@ namespace Squidex.Areas.Api.Controllers.Assets { var suffix = resizeOptions.ToString(); - using (Profiler.Trace("Resize")) + using (Telemetry.Activities.StartActivity("Resize")) { using (var sourceStream = GetTempStream()) { using (var destinationStream = GetTempStream()) { - using (Profiler.Trace("ResizeDownload")) + using (Telemetry.Activities.StartActivity("ResizeDownload")) { await assetFileStore.DownloadAsync(asset.AppId.Id, asset.Id, asset.FileVersion, null, sourceStream); sourceStream.Position = 0; } - using (Profiler.Trace("ResizeImage")) + using (Telemetry.Activities.StartActivity("ResizeImage")) { try { @@ -231,7 +231,7 @@ namespace Squidex.Areas.Api.Controllers.Assets try { - using (Profiler.Trace("ResizeUpload")) + using (Telemetry.Activities.StartActivity("ResizeUpload")) { await assetFileStore.UploadAsync(asset.AppId.Id, asset.Id, asset.FileVersion, suffix, destinationStream, overwrite); destinationStream.Position = 0; diff --git a/backend/src/Squidex/Config/Domain/TelemetryServices.cs b/backend/src/Squidex/Config/Domain/TelemetryServices.cs new file mode 100644 index 000000000..da524a005 --- /dev/null +++ b/backend/src/Squidex/Config/Domain/TelemetryServices.cs @@ -0,0 +1,32 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; + +namespace Squidex.Config.Domain +{ + public static class TelemetryServices + { + public static void AddSquidexTelemetry(this IServiceCollection services) + { + services.AddOpenTelemetryTracing(builder => + { + builder.SetResourceBuilder( + ResourceBuilder.CreateDefault() + .AddService("Squidex", "Squidex", + typeof(TelemetryServices).Assembly.GetName().Version!.ToString())); + + builder.AddSource("Squidex"); + + builder.AddAspNetCoreInstrumentation(); + builder.AddHttpClientInstrumentation(); + }); + } + } +} diff --git a/backend/src/Squidex/Squidex.csproj b/backend/src/Squidex/Squidex.csproj index 259b10948..31c6742ad 100644 --- a/backend/src/Squidex/Squidex.csproj +++ b/backend/src/Squidex/Squidex.csproj @@ -32,16 +32,16 @@ - + - - - - - + + + + + @@ -50,18 +50,20 @@ - - + + - + - + + + - + diff --git a/backend/src/Squidex/Startup.cs b/backend/src/Squidex/Startup.cs index db87f9677..7748339b0 100644 --- a/backend/src/Squidex/Startup.cs +++ b/backend/src/Squidex/Startup.cs @@ -68,6 +68,7 @@ namespace Squidex services.AddSquidexSerializers(); services.AddSquidexStoreServices(config); services.AddSquidexSubscriptions(config); + services.AddSquidexTelemetry(); services.AddSquidexTranslation(config); services.AddSquidexUsageTracking(config); } diff --git a/backend/src/Squidex/appsettings.json b/backend/src/Squidex/appsettings.json index a3756d711..a61a797ca 100644 --- a/backend/src/Squidex/appsettings.json +++ b/backend/src/Squidex/appsettings.json @@ -1,785 +1,546 @@ { - "mode": { - /* - * Use this flag to set Squidex to readonly, e.g. when you deploy a second instance for migration. - */ - "isReadonly": false - }, - - "urls": { - /* - * Set the base url of your application, to generate correct urls in background process. - */ - "baseUrl": "https://localhost:5001", - - /* - * The base path when running Squidex behind a reverse proxy like nginx under a subfolder / subpath. - */ - "basePath": "", - - /* - * Set it to true to redirect the user from http to https permanently. - */ - "enforceHttps": false, - - /* - * Set it to true to return a 400 if the host does not match. - */ - "enforceHost": false, - - /* - * A list of known proxies to make forward headers safer. - */ - "knownProxies": [], - - /* - * Set it to true to use the X-Forwarded- headers for host name and scheme. - */ - "enableForwardHeaders": true, - - /* - * A list of trusted hosts for redirects. - */ - "trustedHosted": [] - }, - - "fullText": { - /* - * Define the type of the full text store. - * - * Supported: elastic (ElasticSearch). Default: MongoDB - */ - "type": "default", - "elastic": { - /* - * The configuration to your elastic search cluster. - * - * Read More: https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/client-configuration.html - */ - "configuration": "http://localhost:9200", - /* - * The name of the index. - */ - "indexName": "squidex" - } - }, - - /* - * Define optional paths to plugins. - */ - "plugins": [ - "Squidex.Extensions.dll" - ], - - "caching": { - /* - * Set to true, to use strong etags. - */ - "strongETag": false, - - /* - * Restrict the surrogate keys to the number of characters. - */ - "maxSurrogateKeysSize": 0, - - "replicated": { - /* - * Set to true to enable a replicated cache for app, schemas and rules. Increases performance but reduces consistency. - */ - "enable": true - } - }, - - "languages": { - /* - * Use custom languages where the key is the language code and the value is the english name. - */ - "custom": "" - }, - - "rules": { - /* - * The timeout to execute rule actions. - */ - "executionTimeoutInSeconds": 10 - }, - - "ui": { - /* - * Regex suggestions for the UI - */ - "regexSuggestions": { - // Regex for emails. - "Email": "^[a-zA-Z0-9.!#$%&’*+\\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)*$", - // Regex for phone numbers. - "Phone": "^\\(*\\+*[1-9]{0,3}\\)*-*[1-9]{0,3}[-. /]*\\(*[2-9]\\d{2}\\)*[-. /]*\\d{3}[-. /]*\\d{4} *e*x*t*\\.* *\\d{0,4}$", - // Regex for slugs (e.g. hello-world). - "Slug": "^[a-z0-9]+(\\-[a-z0-9]+)*$", - // Regex for urls. - "Url": "^(?:http(s)?:\\/\\/)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:\\/?#%[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$" + "mode": { + // Use this flag to set Squidex to readonly, e.g. when you deploy a second instance for migration. + "isReadonly": false }, - /* - * True if only admins should be able to create apps. - */ - "onlyAdminsCanCreateApps": false, - - "map": { - /* - * Define the type of the geolocation service. - * - * Supported: GoogleMaps, OSM - */ - "type": "OSM", - "googleMaps": { - /* - * The optional google maps API key. CREATE YOUR OWN PLEASE. - */ - "key": "AIzaSyB_Z8l3nwUxZhMJykiDUJy6bSHXXlwcYMg" - } + "urls": { + // Set the base url of your application, to generate correct urls in background process. + "baseUrl": "https://localhost:5001", + + // The base path when running Squidex behind a reverse proxy like nginx under a subfolder / subpath. + "basePath": "", + + // Set it to true to redirect the user from http to https permanently. + "enforceHttps": false, + + // Set it to true to return a 400 if the host does not match. + "enforceHost": false, + + // A list of known proxies to make forward headers safer. + "knownProxies": [], + + // Set it to true to use the X-Forwarded- headers for host name and scheme. + "enableForwardHeaders": true, + + // A list of trusted hosts for redirects. + "trustedHosted": [] }, - /* - * Redirect to login automatically. - */ - "redirectToLogin": false, - - /* - * Hide the news dialog. - */ - "hideNews": false, - - /* - * Hide all onboarding tooltips and dialogs. - */ - "hideOnboarding": false, - - /* - * Hide the today and now button. - */ - "hideDateButtons": false, - - /* - * Hide the Local/UTC button - */ - "hideDateTimeModeButton": false, - - /* - * Show the exposed values as information on the apps overview page. - */ - "showInfo": false, - - /* - * The number of content items for dropdown selector. - */ - "referencesDropdownItemCount": 100, - - "google": { - /* - * The Google analytics ID. - */ - "analyticsId": "UA-99989790-2" - } - }, - - "email": { - "smtp": { - /* - * The host name to your email server. - */ - "server": "", - - /* - * The sender email address. - */ - "sender": "hello@squidex.io", - - /* - * The username to authenticate to your email server. - */ - "username": "", - - /* - * The password to authenticate to your email server. - */ - "password": "", - - /* - * Always use SSL if possible. - */ - "enableSsl": true, - - /* - * The port to your email server. - */ - "port": 587 + "fullText": { + // Define the type of the full text store. + // + // SUPPORTED: elastic (ElasticSearch), default. Default: default + "type": "default", + + "elastic": { + // he configuration to your elastic search cluster. + // + // Read More: https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/client-configuration.html + "configuration": "http://localhost:9200", + // The name of the index. + "indexName": "squidex" + } }, - "notifications": { - /* - * The email subject when a new user is added as contributor. - */ - "newUserSubject": "You have been invited to join Project $APP_NAME at Squidex CMS", - - /* - * The email body when a new user is added as contributor. - */ - "newUserBody": "Welcome to Squidex\r\nDear User,\r\n\r\n$ASSIGNER_NAME ($ASSIGNER_EMAIL) has invited you to join Project (also called an App) $APP_NAME at Squidex Headless CMS. Login with your Github, Google or Microsoft credentials to create a new user account and start editing content now.\r\n\r\nThank you very much,\r\nThe Squidex Team\r\n\r\n<> [$UI_URL]", - - /* - * The email subject when an existing user is added as contributor. - */ - "existingUserSubject": "[Squidex CMS] You have been invited to join App $APP_NAME", - - /* - * The email body when an existing user is added as contributor. - */ - "existingUserBody": "Dear User,\r\n\r\n$ASSIGNER_NAME ($ASSIGNER_EMAIL) has invited you to join App $APP_NAME at Squidex Headless CMS.\r\n\r\nLogin or reload the Management UI to see the App.\r\n\r\nThank you very much,\r\nThe Squidex Team\r\n\r\n<> [$UI_URL]", - - /* - * The email subject when app usage reached - */ - "usageSubject": "[Squidex CMS] You you are about to reach your usage limit for App $APP_NAME", - - /* - * The email body when app usage reached - */ - "usageBody": "Dear User,\r\n\r\nYou you are about to reach your usage limit for App $APP_NAME at Squidex Headless CMS.\r\n\r\nYou have already used $API_CALLS of your monthy limit of $API_CALLS_LIMIT API calls.\r\n\r\nPlease check your clients or upgrade your plan!\r\n\r\n<> [$UI_URL]" - } - }, - - /* - * Configure notifo if you want to have support for custom notifications. - */ - "notifo": { - /* - * The id of the app in notifo. - */ - "appId": "", - /* - * The API key for your app in notifo. - */ - "apiKey": "", - /* - * The API URL. - */ - "apiUrl": "https://app.notifo.io" - }, - - "robots": { - /* - * The text for the robots.txt file - */ - "text": "User-agent: *\nAllow: /api/assets/*" - }, - - "healthz": { - "gc": { - /* - * The maximum number of megabyte that the process can consume until it is marked as not healthy. - */ - "threshold": 4096 - } - }, - - "contents": { - /* - * The default page size if not specified by a query. - */ - "defaultPageSize": 200, - - /* - * The maximum number of items to return for each query. - * - * Warning: Use pagination and not large number of items. - */ - "maxResults": 200, - - /* - * The timeout when searching for single items in the database. - */ - "timeoutFind": "00:00:01", - - /* - * The timeout when searching for multiple items in the database. - */ - "timeoutQuery": "00:00:05" - }, - - "assets": { - /* - * The default page size if not specified by a query. - */ - "defaultPageSize": 200, - - /* - * The maximum number of items to return for each query. - * - * Warning: Use pagination and not large number of items. - */ - "maxResults": 200, - - /* - * The maximum file size in bytes. Default: 5MB - */ - "maxSize": 5242880, - - /* - * True to delete assets recursively. - */ - "deleteRecursive": true, - - /* - * True to delete assets files permanently. - */ - "deletePermanent": false, - - /* - * The timeout when searching for single items in the database. - */ - "timeoutFind": "00:00:01", - - /* - * The timeout when searching for multiple items in the database. - */ - "timeoutQuery": "00:00:05", - - /* - * Create one folder per app. - * - * WARNING: If you change this parameter, previous assets are not available anymore. - */ - "folderPerApp": false - }, - - "logging": { - /* - * The log level. - * - * Trace, Debug, Information, Warning, Error, Fatal - */ - "level": "Information", - - /* - * Setting the flag to true, enables well formatteds json logs. - */ - "human": true, - - /* - * Set to true, to use colors. - */ - "colors": true, - - /* - * Set to false to disable logging of http requests. - */ - "logRequests": true, - - /* - * Set to true to enable logging of profiler information. - */ - "logProfiler": false, - - /* - * False to disable the log store. - */ - "storeEnabled": true, - - /* - * The number of days request log items will be stored. - */ - "storeRetentationInDays": 90, - - /* - * True, to enable datadog integration. - */ - "datadog": false, - - /* - * True, to enable application insights integraon. - */ - "applicationInsights": false - }, - - "assetStore": { - /* - * Define the type of the read store. - * - * Supported: Folder (local folder), MongoDb (GridFS), GoogleCloud (hosted in Google Cloud only), AzureBlob, AmazonS3, FTP (not recommended). - */ - "type": "Folder", - "folder": { - /* - * The relative or absolute path to the folder to store the assets. - */ - "path": "Assets" + + // Define optional paths to plugins. + "plugins": [ + "Squidex.Extensions.dll" + ], + + "caching": { + // Set to true, to use strong etags. + "strongETag": false, + + // Restrict the surrogate keys to the number of characters. + "maxSurrogateKeysSize": 0, + + "replicated": { + // Set to true to enable a replicated cache for app, schemas and rules. Increases performance but reduces consistency. + "enable": true + } + }, + + "languages": { + // Use custom languages where the key is the language code and the value is the english name. + "custom": "" + }, + + "rules": { + // The timeout to execute rule actions. + "executionTimeoutInSeconds": 10 + }, + + "ui": { + // Regex suggestions for the UI + "regexSuggestions": { + // Regex for emails. + "Email": "^[a-zA-Z0-9.!#$%&’*+\\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)*$", + // Regex for phone numbers. + "Phone": "^\\(*\\+*[1-9]{0,3}\\)*-*[1-9]{0,3}[-. /]*\\(*[2-9]\\d{2}\\)*[-. /]*\\d{3}[-. /]*\\d{4} *e*x*t*\\.* *\\d{0,4}$", + // Regex for slugs (e.g. hello-world). + "Slug": "^[a-z0-9]+(\\-[a-z0-9]+)*$", + // Regex for urls. + "Url": "^(?:http(s)?:\\/\\/)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:\\/?#%[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$" + }, + + // True if only admins should be able to create apps. + "onlyAdminsCanCreateApps": false, + + "map": { + // Define the type of the geolocation service. + // + // SUPPORTED: GoogleMaps, OSM + "type": "OSM", + + "googleMaps": { + // The optional google maps API key. CREATE YOUR OWN PLEASE. + "key": "AIzaSyB_Z8l3nwUxZhMJykiDUJy6bSHXXlwcYMg" + } + }, + + // Redirect to login automatically. + "redirectToLogin": false, + + // Hide the news dialog. + "hideNews": false, + + // Hide all onboarding tooltips and dialogs. + "hideOnboarding": false, + + // Hide the today and now button. + "hideDateButtons": false, + + // Hide the Local/UTC button + "hideDateTimeModeButton": false, + + // Show the exposed values as information on the apps overview page. + "showInfo": false, + + // The number of content items for dropdown selector. + "referencesDropdownItemCount": 100, + + "google": { + // The Google analytics ID. + "analyticsId": "UA-99989790-2" + } }, - "googleCloud": { - /* - * The name of the bucket in google cloud store. - */ - "bucket": "squidex-assets" + + "email": { + "smtp": { + // The host name to your email server. + "server": "", + + // The sender email address. + "sender": "hello@squidex.io", + + // The username to authenticate to your email server. + "username": "", + + // The password to authenticate to your email server. + "password": "", + + // Always use SSL if possible. + "enableSsl": true, + + // The port to your email server. + "port": 587 + }, + "notifications": { + // The email subject when a new user is added as contributor. + "newUserSubject": "You have been invited to join Project $APP_NAME at Squidex CMS", + + // The email body when a new user is added as contributor. + "newUserBody": "Welcome to Squidex\r\nDear User,\r\n\r\n$ASSIGNER_NAME ($ASSIGNER_EMAIL) has invited you to join Project (also called an App) $APP_NAME at Squidex Headless CMS. Login with your Github, Google or Microsoft credentials to create a new user account and start editing content now.\r\n\r\nThank you very much,\r\nThe Squidex Team\r\n\r\n<> [$UI_URL]", + + // The email subject when an existing user is added as contributor. + "existingUserSubject": "[Squidex CMS] You have been invited to join App $APP_NAME", + + // The email body when an existing user is added as contributor. + "existingUserBody": "Dear User,\r\n\r\n$ASSIGNER_NAME ($ASSIGNER_EMAIL) has invited you to join App $APP_NAME at Squidex Headless CMS.\r\n\r\nLogin or reload the Management UI to see the App.\r\n\r\nThank you very much,\r\nThe Squidex Team\r\n\r\n<> [$UI_URL]", + + // The email subject when app usage reached + "usageSubject": "[Squidex CMS] You you are about to reach your usage limit for App $APP_NAME", + + // The email body when app usage reached + "usageBody": "Dear User,\r\n\r\nYou you are about to reach your usage limit for App $APP_NAME at Squidex Headless CMS.\r\n\r\nYou have already used $API_CALLS of your monthy limit of $API_CALLS_LIMIT API calls.\r\n\r\nPlease check your clients or upgrade your plan!\r\n\r\n<> [$UI_URL]" + } }, - "azureBlob": { - /* - * The name of the container in the Azure Blob Storage - */ - "containerName": "squidex-assets", - - /* - * The connection string to the azure storage service. - */ - "connectionString": "UseDevelopmentStorage=true" + + // Configure notifo if you want to have support for custom notifications. + "notifo": { + // The id of the app in notifo. + "appId": "", + // The API key for your app in notifo. + "apiKey": "", + // The API URL. + "apiUrl": "https://app.notifo.io" }, - "AmazonS3": { - /* - * The url of the S3 API service. Leave it empty if using the one provided by Amazon - */ - "serviceUrl": "", - - /* - * The name of your bucket. - */ - "bucket": "squidex-test", - - /* - * The optional folder within the bucket. - */ - "bucketFolder": "squidex-assets", - - /* - * The region name of your bucket. - */ - "regionName": "eu-central-1", - - /* - * The access key for your user. - * - * Read More: https://supsystic.com/documentation/id-secret-access-key-amazon-s3/ - */ - "accessKey": "", - - /* - * The secret key for your user. - * - * Read More: https://supsystic.com/documentation/id-secret-access-key-amazon-s3/ - */ - "secretKey": "", - - /* - * Force path style property for AmazonS3Config - */ - "forcePathStyle": false + + "robots": { + // The text for the robots.txt file + "text": "User-agent: *\nAllow: /api/assets/*" }, - "mongoDb": { - /* - * The connection string to your Mongo Server. - * - * Read More: https://docs.mongodb.com/manual/reference/connection-string/ - */ - "configuration": "mongodb://localhost", - - /* - * The name of the event store database. - */ - "database": "SquidexAssets", - - /* - * The name of the Mongo Grid FS bucket. - */ - "bucket": "fs" + + "healthz": { + "gc": { + // The maximum number of megabyte that the process can consume until it is marked as not healthy. + "threshold": 4096 + } }, - "ftp": { - /* - *The host of the ftp service - */ - "serverHost": "", - - /* - *The host of the ftp service - */ - "serverPort": "21", - - /* - * Credentials. - */ - "username": "", - "password": "", - - /* - * The relative or absolute path to the folder to store the assets. - */ - "path": "Assets" + + "contents": { + // The default page size if not specified by a query. + "defaultPageSize": 200, + + // The maximum number of items to return for each query. + // + // Warning: Use pagination and not large number of items. + "maxResults": 200, + + // The timeout when searching for single items in the database. + "timeoutFind": "00:00:01", + + // The timeout when searching for multiple items in the database. + "timeoutQuery": "00:00:05" }, - /* - * Allow to expose the url in graph ql url. - */ - "exposeSourceUrl": false - }, - - "orleans": { - /* - * Define the clustering type. - * - * Supported: MongoDB, Development - */ - "clustering": "Development", - - /* - * Tell Orleans it is running in kubernetes. - * - * Read more: https://dotnet.github.io/orleans/docs/deployment/kubernetes.html?q=kubernetes - */ - "kubernetes": false, - - /* - * The port is used to share messages between all cluster members. Must be accessible within your cluster or network. - */ - "siloPort": "11111", - - /* - * The ports used by Orleans to connect to external clients. Not used. - */ - "gatewayPort": "40000", - - /* - * The advertised IP address. Usually not needed. - */ - "ipAddress": "" - }, - - "eventStore": { - /* - * Define the type of the event store. - * - * Supported: MongoDb - */ - "type": "MongoDb", - "mongoDb": { - /* - * The connection string to your Mongo Server. - * - * Read More: https://docs.mongodb.com/manual/reference/connection-string/ - */ - "configuration": "mongodb://localhost", - - /* - * The name of the event store database. - */ - "database": "Squidex" - } - }, - - "eventPublishers": { - /* - * Additional event publishers (advanced usage only): (Name => Config) - */ - "allToRabbitMq": { - /* - * Example:: Push all events to RabbitMq. - */ - "type": "RabbitMq", - "configuration": "amqp://guest:guest@localhost/", - "exchange": "squidex", - "enabled": false, - "eventsFilter": ".*" - } - }, - - "store": { - /* - * Define the type of the read store. - * - * Supported: MongoDb - */ - "type": "MongoDb", - "mongoDb": { - /* - * The connection string to your Mongo Server. - * - * Read More: https://docs.mongodb.com/manual/reference/connection-string/ - */ - "configuration": "mongodb://localhost", - - /* - * The database for all your content collections (one collection per app). - */ - "contentDatabase": "SquidexContent", - - /* - * The database for all your other read collections. - */ - "database": "Squidex" - } - }, - - "identity": { - /* - * Set to true to show PII (Personally Identifiable Information) in the logs. - */ - "showPII": true, - - /* - * Enable password auth. Set this to false if you want to disable local login, leaving only 3rd party login options. - */ - "allowPasswordAuth": true, - - /* - * Initial admin user. - */ - "adminEmail": "", - "adminPassword": "", - - /* - * Recreate the admin if it does not exist or the password does not match. - */ - "adminRecreate": false, - - /* - * Client with all admin permissions. - */ - "adminClientId": "", - "adminClientSecret": "", - - /* - * The apps which should be visible on the dashboard for the admin. - */ - "adminApps": [], - - /* - * Settings for Google auth (keep empty to disable). - */ - "googleClient": "1006817248705-t3lb3ge808m9am4t7upqth79hulk456l.apps.googleusercontent.com", - "googleSecret": "QsEi-fHqkGw2_PjJmtNHf2wg", - - /* - * Settings for Github auth (keep empty to disable). - */ - "githubClient": "211ea00e726baf754c78", - "githubSecret": "d0a0d0fe2c26469ae20987ac265b3a339fd73132", - - /* - * Settings for Microsoft auth (keep empty to disable). - * Tennant is optional for using a specific AzureAD tenant - */ - "microsoftClient": "b55da740-6648-4502-8746-b9003f29d5f1", - "microsoftSecret": "idWbANxNYEF4cB368WXJhjN", - "microsoftTenant": null, - - /* - * Settings for your custom oidc server. - */ - "oidcName": "OIDC", - "oidcAuthority": "", - "oidcClient": "", - "oidcSecret": "", - "oidcMetadataAddress": "", - "oidcScopes": [ - "email" - ], - "oidcResponseType": "id_token", // or "code" - "oidcGetClaimsFromUserInfoEndpoint": false, - "oidcOnSignoutRedirectUrl": "", - - /* - * Lock new users automatically, the administrator must unlock them. - */ - "lockAutomatically": false, - - /* - * The url to you privacy statements, if you host squidex by yourself. - */ - "privacyUrl": "https://squidex.io/privacy" - }, - - "news": { - /* - * The app name where the news are stored. - */ - "appName": "squidex-website", - - /* - * The credentials to the app (Readonly). - */ - "clientId": "squidex-website:default", - "clientSecret": "QGgqxd7bDHBTEkpC6fj8sbdPWgZrPrPfr3xzb3LKoec=" - }, - - "translations": { - "deepl": { - /* - * The deepl api key if you want to support automated translations. - */ - "authKey": "", - "mapping": { - "zh-TW": "zh-TW", - "zh-CN": "zh-CN" - } + + "assets": { + // The default page size if not specified by a query. + "defaultPageSize": 200, + + // The maximum number of items to return for each query. + // + // Warning: Use pagination and not large number of items. + "maxResults": 200, + + // The maximum file size in bytes. Default: 5MB + "maxSize": 5242880, + + // True to delete assets recursively. + "deleteRecursive": true, + + // True to delete assets files permanently. + "deletePermanent": false, + + // The timeout when searching for single items in the database. + "timeoutFind": "00:00:01", + + // The timeout when searching for multiple items in the database. + "timeoutQuery": "00:00:05", + + // Create one folder per app. + // + // WARNING: If you change this parameter, previous assets are not available anymore. + "folderPerApp": false + }, + + "logging": { + // The log level. + // + // Trace, Debug, Information, Warning, Error, Fatal + "level": "Information", + + // Setting the flag to true, enables well formatteds json logs. + "human": true, + + // Set to true, to use colors. + "colors": true, + + // Set to false to disable logging of http requests. + "logRequests": true, + + // False to disable the log store. + "storeEnabled": true, + + // The number of days request log items will be stored. + "storeRetentationInDays": 90, + + "stackdriver": { + // True, to enable stackdriver integration. + "enabled": false + }, + + "otlp": { + // True, to enable OpenTelemetry Protocol integration. + "enabled": false, + + // The endpoint to the agent. + "endpoint": "" + }, + + "applicationInsights": { + // True, to enable application insights integraon. + "enabled": false, + + "connectionString": "InstrumentationKey=[key];IngestionEndpoint=https://[datacenter].in.applicationinsights.azure.com/" + } + }, + + "assetStore": { + // Define the type of the read store. + // + // SUPPORTED: Folder (local folder), MongoDb (GridFS), GoogleCloud (hosted in Google Cloud only), AzureBlob, AmazonS3, FTP (not recommended). + "type": "Folder", + + "folder": { + // The relative or absolute path to the folder to store the assets. + "path": "Assets" + }, + "googleCloud": { + // The name of the bucket in google cloud store. + "bucket": "squidex-assets" + }, + "azureBlob": { + // The name of the container in the Azure Blob Storage + "containerName": "squidex-assets", + + // The connection string to the azure storage service. + "connectionString": "UseDevelopmentStorage=true" + }, + "amazonS3": { + // The url of the S3 API service. Leave it empty if using the one provided by Amazon + "serviceUrl": "", + + // The name of your bucket. + "bucket": "squidex-test", + + // The optional folder within the bucket. + "bucketFolder": "squidex-assets", + + // The region name of your bucket. + "regionName": "eu-central-1", + + // The access key for your user. + // + // Read More: https://supsystic.com/documentation/id-secret-access-key-amazon-s3/ + "accessKey": "", + + // The secret key for your user. + // + // Read More: https://supsystic.com/documentation/id-secret-access-key-amazon-s3/ + "secretKey": "", + + // Force path style property for AmazonS3Config + "forcePathStyle": false + }, + "mongoDb": { + // The connection string to your Mongo Server. + // + // Read More: https://docs.mongodb.com/manual/reference/connection-string/ + "configuration": "mongodb://localhost", + + // The name of the event store database. + "database": "SquidexAssets", + + // The name of the Mongo Grid FS bucket. + "bucket": "fs" + }, + "ftp": { + //The host of the ftp service + "serverHost": "", + + //The host of the ftp service + "serverPort": "21", + + // Credentials. + "username": "", + "password": "", + + // The relative or absolute path to the folder to store the assets. + "path": "Assets" + }, + // Allow to expose the url in graph ql url. + "exposeSourceUrl": false + }, + + "orleans": { + // Define the clustering type. + // + // SUPPORTED: MongoDB, Development + "clustering": "Development", + + // Tell Orleans it is running in kubernetes. + // + // Read more: https://dotnet.github.io/orleans/docs/deployment/kubernetes.html?q=kubernetes + "kubernetes": false, + + // The port is used to share messages between all cluster members. Must be accessible within your cluster or network. + "siloPort": "11111", + + // The ports used by Orleans to connect to external clients. Not used. + "gatewayPort": "40000", + + // The advertised IP address. Usually not needed. + "ipAddress": "" + }, + + "eventStore": { + // Define the type of the event store. + // + // SUPPORTED: MongoDb + "type": "MongoDb", + + "mongoDb": { + // The connection string to your Mongo Server. + // + // Read More: https://docs.mongodb.com/manual/reference/connection-string/ + "configuration": "mongodb://localhost", + + // The name of the event store database. + "database": "Squidex" + } + }, + + "eventPublishers": { + // Additional event publishers (advanced usage only): (Name => Config) + "allToRabbitMq": { + // Example:: Push all events to RabbitMq. + "type": "RabbitMq", + "configuration": "amqp://guest:guest@localhost/", + "exchange": "squidex", + "enabled": false, + "eventsFilter": ".*" + } + }, + + "store": { + // Define the type of the read store. + // + // SUPPORTED: MongoDb + "type": "MongoDb", + + "mongoDb": { + // The connection string to your Mongo Server. + // + // Read More: https://docs.mongodb.com/manual/reference/connection-string/ + "configuration": "mongodb://localhost", + + // The database for all your content collections (one collection per app). + "contentDatabase": "SquidexContent", + + // The database for all your other read collections. + "database": "Squidex" + } + }, + + "identity": { + // Set to true to show PII (Personally Identifiable Information) in the logs. + "showPII": true, + + // Enable password auth. Set this to false if you want to disable local login, leaving only 3rd party login options. + "allowPasswordAuth": true, + + // Initial admin user. + "adminEmail": "", + "adminPassword": "", + + // Recreate the admin if it does not exist or the password does not match. + "adminRecreate": false, + + // Client with all admin permissions. + "adminClientId": "", + "adminClientSecret": "", + + // The apps which should be visible on the dashboard for the admin. + "adminApps": [], + + // Settings for Google auth (keep empty to disable). + "googleClient": "1006817248705-t3lb3ge808m9am4t7upqth79hulk456l.apps.googleusercontent.com", + "googleSecret": "QsEi-fHqkGw2_PjJmtNHf2wg", + + // Settings for Github auth (keep empty to disable). + "githubClient": "211ea00e726baf754c78", + "githubSecret": "d0a0d0fe2c26469ae20987ac265b3a339fd73132", + + // Settings for Microsoft auth (keep empty to disable).3 + // + // NOTE: Tennant is optional for using a specific AzureAD tenant + "microsoftClient": "b55da740-6648-4502-8746-b9003f29d5f1", + "microsoftSecret": "idWbANxNYEF4cB368WXJhjN", + "microsoftTenant": null, + + // Settings for your custom oidc server. + "oidcName": "OIDC", + "oidcAuthority": "", + "oidcClient": "", + "oidcSecret": "", + "oidcMetadataAddress": "", + "oidcScopes": [ + "email" + ], + "oidcResponseType": "id_token", // or "code" + "oidcGetClaimsFromUserInfoEndpoint": false, + "oidcOnSignoutRedirectUrl": "", + + // Lock new users automatically, the administrator must unlock them. + "lockAutomatically": false, + + // The url to you privacy statements, if you host squidex by yourself. + "privacyUrl": "https://squidex.io/privacy" + }, + + "news": { + // The app name where the news are stored. + "appName": "squidex-website", + + // The credentials to the app (Readonly). + "clientId": "squidex-website:default", + "clientSecret": "QGgqxd7bDHBTEkpC6fj8sbdPWgZrPrPfr3xzb3LKoec=" + }, + + "translations": { + "deepl": { + // The deepl api key if you want to support automated translations. + "authKey": "", + "mapping": { + "zh-TW": "zh-TW", + "zh-CN": "zh-CN" + } + }, + + "googleCloud": { + // The google cloud project id if you want to support automated translations. + "projectId": "" + } + }, + + "rebuild": { + // Set to true to rebuild apps. + "apps": false, + + // Set to true to rebuild assets. + "assets": false, + + // Set to true to create dummy asset files if they do not exist. Useful when a backup fail. + "assetFiles": false, + + // Set to true to rebuild contents. + "contents": false, + + // Set to true to rebuild rules. + "rules": false, + + // Set to true to rebuild schemas. + "schemas": false, + + // Set to true to rebuild indexes. + "indexes": false + }, + + // A list of configuration values that should be exposed from the info endpoint and in the UI. + "exposedConfiguration": { + "version": "squidex:version" + }, + + // Kafka Producer configuration + "kafka": { + "bootstrapServers": "" }, - "googleCloud": { - /* - * The google cloud project id if you want to support automated translations. - */ - "projectId": "" + // The client information for twitter. + "twitter": { + "clientId": "QZhb3HQcGCvE6G8yNNP9ksNet", + "clientSecret": "Pdu9wdN72T33KJRFdFy1w4urBKDRzIyuKpc0OItQC2E616DuZD" } - }, - - "rebuild": { - /* - * Set to true to rebuild apps. - */ - "apps": false, - - /* - * Set to true to rebuild assets. - */ - "assets": false, - - /* - * Set to true to create dummy asset files if they do not exist. Useful when a backup fail. - */ - "assetFiles": false, - - /* - * Set to true to rebuild contents. - */ - "contents": false, - - /* - * Set to true to rebuild rules. - */ - "rules": false, - - /* - * Set to true to rebuild schemas. - */ - "schemas": false, - - /* - * Set to true to rebuild indexes. - */ - "indexes": false - }, - - /*" - * A list of configuration values that should be exposed from the info endpoint and in the UI. - */ - "exposedConfiguration": { - "version": "squidex:version" - }, - - /* - * Kafka Producer configuration - */ - "kafka": { - "bootstrapServers": "" - }, - - /* - * The client information for twitter. - */ - "twitter": { - "clientId": "QZhb3HQcGCvE6G8yNNP9ksNet", - "clientSecret": "Pdu9wdN72T33KJRFdFy1w4urBKDRzIyuKpc0OItQC2E616DuZD" - } }