internal void Populate(ExceptionalSettings settings) { var s = settings.Store; s.Type = Type; if (Path.HasValue()) { s.Path = Path; } if (ConnectionString.HasValue()) { s.ConnectionString = ConnectionString; } if (ConnectionStringName.HasValue()) { s.ConnectionString = ConfigurationManager.ConnectionStrings[ConnectionStringName]?.ConnectionString ?? throw new ConfigurationErrorsException("A connection string was not found for the connection string name provided: " + ConnectionStringName); } if (Size.HasValue) { s.Size = Size.Value; } if (RollupSeconds.HasValue) { s.RollupPeriod = TimeSpan.FromSeconds(RollupSeconds.Value); } if (BackupQueueSize.HasValue) { s.BackupQueueSize = BackupQueueSize.Value; } }
/// <summary> /// Binds an <see cref="IConfiguration"/> to an <see cref="ExceptionalSettings"/> object. /// This happens with a normal .Bind() followed by the complex type mappings manually. /// </summary> /// <param name="config">The <see cref="IConfigurationSection"/> to bind.</param> /// <param name="settings">The <see cref="ExceptionalSettings"/> to bind to.</param> public static void Bind(this IConfiguration config, ExceptionalSettings settings) { // Bind the simple types (almost everything) ConfigurationBinder.Bind(config, settings); // because we overrode .Bind() here // Now, explicitly bind the complex types var dataIncludePattern = config.GetValue <string>(nameof(ExceptionalSettings.DataIncludeRegex)); if (!string.IsNullOrEmpty(dataIncludePattern)) { settings.DataIncludeRegex = new Regex(dataIncludePattern, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); } var ignoreRegexes = config.GetSection(nameof(ExceptionalSettings.Ignore)) .GetSection(nameof(ExceptionalSettingsBase.IgnoreSettings.Regexes)) .AsEnumerable(); foreach (var ir in ignoreRegexes) { if (ir.Value != null) { settings.Ignore.Regexes.Add(new Regex(ir.Value, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline)); } } // If email is configured, hook it up if (settings.Email.ToAddress.HasValue()) { settings.Register(new EmailNotifier(settings.Email)); } }
static Exceptional() { if (Settings == null) { Settings = new ExceptionalSettings(); } }
/// <summary> /// Asynchronously logs an exception to the configured error store, or the in-memory default store if none is configured. /// </summary> /// <param name="ex">The exception to log.</param> /// <param name="context">The HTTPContext to record variables from. If this isn't a web request, pass <see langword="null" /> in here.</param> /// <param name="category">The category to associate with this exception.</param> /// <param name="rollupPerServer">Whether to log up per-server, e.g. errors are only duplicates if they have same stack on the same machine.</param> /// <param name="customData">Any custom data to store with the exception like UserId, etc...this will be rendered as JSON in the error view for script use.</param> /// <param name="applicationName">If specified, the application name to log with, if not specified the name in <see cref="ErrorStoreSettings.ApplicationName"/> is used.</param> /// <returns>The Error created, if one was created and logged, null if nothing was logged.</returns> /// <remarks> /// When dealing with a non web requests, pass <see langword="null" /> in for context. /// It shouldn't be forgotten for most web application usages, so it's not an optional parameter. /// </remarks> public static Task <Error> LogAsync( this Exception ex, HttpContext context, string category = null, bool rollupPerServer = false, Dictionary <string, string> customData = null, string applicationName = null) { if (Statics.IsLoggingEnabled) { ExceptionalSettings settings = null; try { // If context is null, still log with a fallback...this is far better than going boom. settings = context?.RequestServices.GetRequiredService <IOptions <ExceptionalSettings> >().Value ?? Exceptional.Settings; // If we should be ignoring this exception, skip it entirely. // Otherwise create the error itself, populating CustomData with what was passed-in. var error = ex.GetErrorIfNotIgnored(settings, category, applicationName, rollupPerServer, customData); if (error != null) { // Get everything from the HttpContext error.SetProperties(context); return(LogAsyncCore(error)); } } catch (Exception e) { settings?.OnLogFailure?.Invoke(e); Trace.WriteLine(e); } } return(Task.FromResult <Error>(null)); }
private static void EnsureInit() { if (!(Settings is ExceptionalSettings)) { Settings = new ExceptionalSettings(); } }
/// <summary> /// Configures existing settings (creating them if missing), also making them available for use globally. /// </summary> /// <param name="configSettings">The settings object to set for background settings.</param> public static void Configure(Action <ExceptionalSettings> configSettings) { _ = configSettings ?? throw new ArgumentNullException(nameof(configSettings)); if (Settings == null) { Statics.Settings = Settings = new ExceptionalSettings(); } configSettings?.Invoke(Settings); }
internal void Populate(ExceptionalSettings settings) { var s = settings.LogFilters; foreach (LogFilter f in FormFilters) { s.Form[f.Name] = f.ReplaceWith; } foreach (LogFilter c in CookieFilters) { s.Cookie[c.Name] = c.ReplaceWith; } }
/// <summary> /// Runs after deserialization, to populate <see cref="ExceptionalSettingsBase.Ignore"/>. /// </summary> /// <param name="settings">The <see cref="ExceptionalSettings"/> to populate.</param> internal void Populate(ExceptionalSettings settings) { var s = settings.Ignore; foreach (IgnoreRegex r in Regexes) { if (r.Pattern.HasValue()) { s.Regexes.Add(new Regex(r.Pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline)); } } foreach (IgnoreType t in Types) { s.Types.Add(t.Type); } }
internal void Populate(ExceptionalSettings settings) { var s = settings.Email; if (ToAddress.HasValue()) { s.ToAddress = ToAddress; } if (FromAddress.HasValue()) { s.FromAddress = FromAddress; } if (FromDisplayName.HasValue()) { s.FromDisplayName = FromDisplayName; } if (SMTPHost.HasValue()) { s.SMTPHost = SMTPHost; } if (SMTPPort.HasValue) { s.SMTPPort = SMTPPort; } if (SMTPUserName.HasValue()) { s.SMTPUserName = SMTPUserName; } if (SMTPPassword.HasValue()) { s.SMTPPassword = SMTPPassword; } if (SMTPEnableSSL.HasValue) { s.SMTPEnableSSL = SMTPEnableSSL.Value; } if (PreventDuplicates.HasValue) { s.PreventDuplicates = PreventDuplicates.Value; } if (s.ToAddress.HasValue()) { settings.Register(new EmailNotifier(s)); } }
/// <summary> /// Trigger deserialization, which loads settings from the .config file. /// </summary> public static void LoadSettings() { if (Interlocked.CompareExchange(ref _loaded, 1, 0) == 0) { var settings = new ExceptionalSettings(); var config = ConfigurationManager.GetSection("Exceptional") as Settings; if (config.DataIncludePattern.HasValue()) { settings.DataIncludeRegex = new Regex(config.DataIncludePattern, RegexOptions.Singleline | RegexOptions.Compiled); } config.ErrorStore?.Populate(settings); config.Ignore?.Populate(settings); config.LogFilters?.Populate(settings); config.Email?.Populate(settings); settings.Store.ApplicationName = config.ApplicationName; Exceptional.Configure(settings); } }
internal void Populate(ExceptionalSettings settings) { var s = settings.LogFilters; foreach (LogFilter f in FormFilters) { s.Form[f.Name] = f.ReplaceWith; } foreach (LogFilter c in CookieFilters) { s.Cookie[c.Name] = c.ReplaceWith; } foreach (LogFilter h in HeaderFilters) { s.Header[h.Name] = h.ReplaceWith; } foreach (LogFilter h in QueryStringFilters) { s.QueryString[h.Name] = h.ReplaceWith; } }
/// <summary> /// Logs an exception to the configured error store, or the in-memory default store if none is configured. /// </summary> /// <param name="ex">The exception to log.</param> /// <param name="context">The HTTPContext to record variables from. If this isn't a web request, pass <see langword="null" /> in here.</param> /// <param name="category">The category to associate with this exception.</param> /// <param name="rollupPerServer">Whether to log up per-server, e.g. errors are only duplicates if they have same stack on the same machine.</param> /// <param name="customData">Any custom data to store with the exception like UserId, etc...this will be rendered as JSON in the error view for script use.</param> /// <param name="applicationName">If specified, the application name to log with, if not specified the name in <see cref="ErrorStoreSettings.ApplicationName"/> is used.</param> /// <returns>The Error created, if one was created and logged, null if nothing was logged.</returns> /// <remarks> /// When dealing with a non web requests, pass <see langword="null" /> in for context. /// It shouldn't be forgotten for most web application usages, so it's not an optional parameter. /// </remarks> public static Error Log( this Exception ex, HttpContext context, string category = null, bool rollupPerServer = false, Dictionary <string, string> customData = null, string applicationName = null) { if (Statics.IsLoggingEnabled) { ExceptionalSettings settings = null; try { settings = context.RequestServices.GetRequiredService <IOptions <ExceptionalSettings> >().Value; // If we should be ignoring this exception, skip it entirely. // Otherwise create the error itself, populating CustomData with what was passed-in. var error = ex.GetErrorIfNotIgnored(settings, category, applicationName, rollupPerServer, customData); if (error != null) { // Get everything from the HttpContext error.SetProperties(context); if (error.LogToStore()) { return(error); } } } catch (Exception e) { settings?.OnLogFailure?.Invoke(e); Trace.WriteLine(e); } } return(null); }
/// <summary> /// Saves the given <paramref name="settings"/> as the global <see cref="Settings"/> available for use globally. /// These are intended to be used by global/background handlers where normal context access isn't available. /// </summary> /// <param name="settings">The settings object to set for background settings.</param> public static void Configure(ExceptionalSettings settings) => Statics.Settings = Settings = settings ?? throw new ArgumentNullException(nameof(settings));