diff --git a/Core/Resgrid.Config/ExternalErrorConfig.cs b/Core/Resgrid.Config/ExternalErrorConfig.cs index 17c4b152..fb3cb029 100644 --- a/Core/Resgrid.Config/ExternalErrorConfig.cs +++ b/Core/Resgrid.Config/ExternalErrorConfig.cs @@ -22,6 +22,7 @@ public static class ExternalErrorConfig public static string ExternalErrorServiceUrlForInternalApi = ""; public static string ExternalErrorServiceUrlForInternalWorker = ""; public static string ExternalErrorServiceUrlForMcp = ""; + public static string ExternalErrorServiceUrlForTts = ""; public static double SentryPerfSampleRate = 0.4; public static double SentryProfilingSampleRate = 0; #endregion Sentry Settings diff --git a/Web/Resgrid.Web.Tts/Dockerfile b/Web/Resgrid.Web.Tts/Dockerfile index 0c22b22e..70a1790e 100644 --- a/Web/Resgrid.Web.Tts/Dockerfile +++ b/Web/Resgrid.Web.Tts/Dockerfile @@ -19,8 +19,17 @@ WORKDIR /src/Web/Resgrid.Web.Tts RUN dotnet publish "Resgrid.Web.Tts.csproj" -c Release -o /app/publish /p:UseAppHost=false --no-restore -p:Version=${BUILD_VERSION} FROM base AS final -RUN apt-get update \ - && apt-get install -y --no-install-recommends espeak-ng ffmpeg ca-certificates \ +# Enable multiverse repository for MBROLA packages, install TTS dependencies, +# then clean up in a single RUN layer to keep the image small. +RUN echo "deb http://archive.ubuntu.com/ubuntu/ noble multiverse" >> /etc/apt/sources.list \ + && echo "deb http://archive.ubuntu.com/ubuntu/ noble-updates multiverse" >> /etc/apt/sources.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + espeak-ng \ + ffmpeg \ + ca-certificates \ + mbrola \ + mbrola-us1 \ && rm -rf /var/lib/apt/lists/* \ && groupadd --gid 10001 appgroup \ && useradd --uid 10001 --gid appgroup --create-home --shell /usr/sbin/nologin appuser diff --git a/Web/Resgrid.Web.Tts/Program.cs b/Web/Resgrid.Web.Tts/Program.cs index 59b72f56..2dadf2d7 100644 --- a/Web/Resgrid.Web.Tts/Program.cs +++ b/Web/Resgrid.Web.Tts/Program.cs @@ -9,14 +9,54 @@ using Resgrid.Web.Tts.Configuration; using Resgrid.Web.Tts.Health; using Resgrid.Web.Tts.Services; +using System.Reflection; using System.Text.Json; using System.Threading.RateLimiting; +using Sentry.Profiling; var builder = WebApplication.CreateBuilder(args); ConfigProcessor.LoadAndProcessConfig(builder.Configuration["AppOptions:ConfigPath"]); ConfigProcessor.LoadAndProcessEnvVariables(builder.Configuration.AsEnumerable()); +// Configure Sentry error tracking and performance monitoring +if (!string.IsNullOrWhiteSpace(ExternalErrorConfig.ExternalErrorServiceUrlForTts)) +{ + builder.WebHost.UseSentry(options => + { + options.Dsn = ExternalErrorConfig.ExternalErrorServiceUrlForTts; + options.AttachStacktrace = true; + options.SendDefaultPii = true; + options.AutoSessionTracking = true; + options.TracesSampleRate = ExternalErrorConfig.SentryPerfSampleRate; + options.Environment = ExternalErrorConfig.Environment; + options.Release = Assembly.GetEntryAssembly()?.GetName().Version?.ToString(); + options.ProfilesSampleRate = ExternalErrorConfig.SentryProfilingSampleRate; + + // Add profiling integration for performance tracing + options.AddIntegration(new ProfilingIntegration()); + + options.TracesSampler = samplingContext => + { + if (samplingContext?.CustomSamplingContext != null) + { + if (samplingContext.CustomSamplingContext.TryGetValue("__HttpPath", out var httpPath)) + { + var pathValue = httpPath?.ToString(); + if (string.Equals(pathValue, "/health", StringComparison.OrdinalIgnoreCase) || + string.Equals(pathValue, "/livez", StringComparison.OrdinalIgnoreCase) || + string.Equals(pathValue, "/readyz", StringComparison.OrdinalIgnoreCase)) + { + return 0; + } + } + } + + return ExternalErrorConfig.SentryPerfSampleRate; + }; + }); +} + builder.Logging.ClearProviders(); builder.Logging.AddJsonConsole(); diff --git a/Web/Resgrid.Web.Tts/Resgrid.Web.Tts.csproj b/Web/Resgrid.Web.Tts/Resgrid.Web.Tts.csproj index f12bf054..a8611a39 100644 --- a/Web/Resgrid.Web.Tts/Resgrid.Web.Tts.csproj +++ b/Web/Resgrid.Web.Tts/Resgrid.Web.Tts.csproj @@ -12,6 +12,8 @@ + + diff --git a/Web/Resgrid.Web.Tts/appsettings.json b/Web/Resgrid.Web.Tts/appsettings.json index b3b0679f..9bc5cf00 100644 --- a/Web/Resgrid.Web.Tts/appsettings.json +++ b/Web/Resgrid.Web.Tts/appsettings.json @@ -6,10 +6,17 @@ "Amazon": "Warning" } }, + "Sentry": { + "IncludeActivityData": true, + "MaxBreadcrumbs": 50, + "MinimumBreadcrumbLevel": "Information", + "MinimumEventLevel": "Error", + "Debug": false + }, "AppOptions": { "ConfigPath": "" }, - "_Comment": "TTS runtime settings are loaded from Resgrid.Config.TtsConfig via ResgridConfig.json or RESGRID:TtsConfig:* environment variables.", + "_Comment": "TTS runtime settings are loaded from Resgrid.Config.TtsConfig via ResgridConfig.json or RESGRID:TtsConfig:* environment variables. Sentry DSN uses ExternalErrorConfig.ExternalErrorServiceUrlForTts.", "AllowedHosts": "*", "ConnectionStrings": { }