/* * Copyright (c) 2023 Proton AG * * This file is part of ProtonVPN. * * ProtonVPN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ProtonVPN is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ProtonVPN. If not, see . */ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using ProtonVPN.Builds.Variables; using ProtonVPN.Common.Core.Helpers; using ProtonVPN.Common.Legacy.OS.DeviceIds; using ProtonVPN.IssueReporting.DataScrubbing; using ProtonVPN.IssueReporting.DiagnosticLogging; using ProtonVPN.IssueReporting.HttpHandlers; using ProtonVPN.Logging.Contracts; using ProtonVPN.Logging.Contracts.Events.AppLogs; using Sentry; namespace ProtonVPN.IssueReporting; public static class SentryInitializer { private static bool _isEnabled; private static readonly ISentryDiagnosticLogger _sentryDiagnosticLogger = new SentryDiagnosticLogger(); private static ILogger? _logger; private static readonly SentryDataScrubber _dataScrubber = new(); public static void SetLogger(ILogger logger) { _logger = logger; _sentryDiagnosticLogger?.SetLogger(logger); DeviceIdStaticBuilder.SetLogger(logger); } public static void Run() { SentryOptions options = GetSentryOptions(); SentrySdk.Init(options); } public static void SetEnabled(bool isEnabled) { _isEnabled = isEnabled; } private static SentryOptions GetSentryOptions() { SentryOptions options = new() { Release = $"vpn.windows-{AssemblyVersion.Get()}", AttachStacktrace = true, Dsn = GlobalConfig.SentryDsn, ReportAssembliesMode = ReportAssembliesMode.None, CreateHttpMessageHandler = () => new SentryHttpClientHandler(), AutoSessionTracking = false, Debug = false, DiagnosticLogger = _sentryDiagnosticLogger, IsGlobalModeEnabled = true, SendClientReports = false, }; options.AddEventProcessor(new SentryEventScrubberProcessor()); options.SetBeforeSend(e => { LogSentryEvent(e); if (!_isEnabled) { _logger?.Info("Dropping event, because the user has disabled sending crash reports."); return null; } e.SetTag("ProcessName", Process.GetCurrentProcess().ProcessName); e.SetTag("Architecture", OSArchitecture.StringValue); e.SetTag("WindowsSKU", WindowsSku.GetHexString()); e.User.Id = DeviceIdStaticBuilder.GetDeviceId(); string logs = GetLogs(); e.SetExtra("logs", _dataScrubber.Scrub(logs)); return e; }); return options; } private static void LogSentryEvent(SentryEvent e) { switch (e.Level) { case SentryLevel.Debug: _logger?.Debug(e.Message?.Message); break; case SentryLevel.Info: _logger?.Info(e.Message?.Message); break; case SentryLevel.Warning: _logger?.Warn(e.Message?.Message); break; case SentryLevel.Error: string message = e.Message?.Message!; if (string.IsNullOrEmpty(message)) { message = "Exception handled by Sentry"; } _logger?.Error(message, e.Exception); break; case SentryLevel.Fatal: _logger?.Fatal(e.Message?.Message); break; } } private static string GetLogs() { IList logs = _logger?.GetRecentLogs().Reverse().ToList() ?? []; StringBuilder sb = new(); foreach (string log in logs) { sb.AppendLine(log); } return sb.ToString(); } }