private EventData ToEventData(LogEvent logEvent) { EventData eventData = new EventData { ProviderName = nameof(SerilogInput), Timestamp = logEvent.Timestamp, Level = ToLogLevel[logEvent.Level], Keywords = 0 }; var payload = eventData.Payload; // Prefer the built-in `Message` and `Exception` properties by adding them to the payload // first. If other attached data items have conflicting names, they will be added as // `Message_1` and so-on. if (logEvent.Exception != null) { if (logEvent.Level >= LogEventLevel.Error) { EventMetadata eventMetadata = new EventMetadata(ExceptionData.ExceptionMetadataKind); eventMetadata.Properties.Add(ExceptionData.ExceptionPropertyMoniker, "Exception"); eventData.SetMetadata(eventMetadata); } eventData.AddPayloadProperty("Exception", logEvent.Exception, healthReporter, nameof(SerilogInput)); } // Inability to render the message, or any other LogEvent property, should not stop us from sending the event down the pipeline try { eventData.AddPayloadProperty("Message", logEvent.RenderMessage(), healthReporter, nameof(SerilogInput)); } catch (Exception e) { healthReporter.ReportWarning($"{nameof(SerilogInput)}: event message could not be rendered{Environment.NewLine}{e.ToString()}"); } // MessageTemplate is always present on Serilog events eventData.AddPayloadProperty("MessageTemplate", logEvent.MessageTemplate.Text, healthReporter, nameof(SerilogInput)); foreach (var property in logEvent.Properties.Where(property => property.Value != null)) { try { eventData.AddPayloadProperty(property.Key, ToRawValue(property.Value), healthReporter, nameof(SerilogInput)); } catch (Exception e) { healthReporter.ReportWarning($"{nameof(SerilogInput)}: event property '{property.Key}' could not be rendered{Environment.NewLine}{e.ToString()}"); } } return(eventData); }
public static void AddPayloadProperty(IDictionary <string, object> payload, string key, object value, IHealthReporter healthReporter, string context) { Requires.NotNull(payload, nameof(payload)); Requires.NotNull(key, nameof(key)); Requires.NotNull(healthReporter, nameof(healthReporter)); if (!payload.ContainsKey(key)) { payload.Add(key, value); return; } string newKey; int i = 1; //update property key till there is no such key in dict do { newKey = key + "_" + i.ToString("d"); i++; }while (payload.ContainsKey(newKey)); payload.Add(newKey, value); healthReporter.ReportWarning($"The property with the key '{key}' already exist in the event payload. Value was added under key '{newKey}'", context); }
private EventData ToEventData(LoggingEvent loggingEvent) { var eventData = new EventData { ProviderName = $"{nameof(Log4netInput)}.{loggingEvent.LoggerName}", Timestamp = loggingEvent.TimeStamp, Level = ToLogLevel[loggingEvent.Level], Keywords = 0, Payload = { { "Message", loggingEvent.MessageObject } } }; if (loggingEvent.ExceptionObject != null) { eventData.Payload.Add("Exception", loggingEvent.ExceptionObject); } foreach (var key in loggingEvent.Properties.GetKeys()) { try { eventData.AddPayloadProperty(key, loggingEvent.LookupProperty(key), healthReporter, nameof(Log4netInput)); } catch (Exception ex) { healthReporter.ReportWarning($"{nameof(Log4netInput)}: event property '{key}' could not be rendered{Environment.NewLine}{ex}"); } } return(eventData); }
public TraceInput(IConfiguration configuration, IHealthReporter healthReporter) { Validation.Requires.NotNull(configuration, nameof(configuration)); Validation.Requires.NotNull(healthReporter, nameof(healthReporter)); this.healthReporter = healthReporter; TraceInputConfiguration traceInputConfiguration = new TraceInputConfiguration(); bool configurationIsValid = true; try { configuration.Bind(traceInputConfiguration); } catch { configurationIsValid = false; } if (!configurationIsValid || !string.Equals(traceInputConfiguration.Type, "trace", StringComparison.OrdinalIgnoreCase)) { healthReporter.ReportWarning($"Invalid {nameof(TraceInput)} configuration encountered: '{configuration.ToString()}'. Error will be used as trace level", EventFlowContextIdentifiers.Configuration); } Initialize(traceInputConfiguration); }
void IObserver <TInput> .OnNext(TInput value) { if (!target.Post(value)) { healthReporter.ReportWarning("An event was dropped from the diagnostic pipeline because there was not enough capacity", EventFlowContextIdentifiers.Throttling); } }
private void Initialize(List <EventSourceConfiguration> eventSources, IHealthReporter healthReporter) { this.healthReporter = healthReporter; this.subject = new EventFlowSubject <EventData>(); if (eventSources.Count() == 0) { healthReporter.ReportWarning($"{nameof(EventSourceInput)}: no event sources configured, the input will not produce any data", EventFlowContextIdentifiers.Configuration); } var invalidConfigurationItems = new List <EventSourceConfiguration>(); foreach (var eventSourceConfiguration in eventSources) { if (!eventSourceConfiguration.Validate()) { healthReporter.ReportProblem($"{nameof(EventSourceInput)}: configuration for one of the sources is invalid", EventFlowContextIdentifiers.Configuration); invalidConfigurationItems.Add(eventSourceConfiguration); } } // eventSources is a collection created by us, so we can modify it as necessary eventSources.RemoveAll(config => invalidConfigurationItems.Contains(config)); // Special case: because of .NET bug https://github.com/dotnet/coreclr/issues/14434, using Microsoft-ApplicationInsights-Data will result in infinite loop. // So we will disable it by default, unless there is explicit configuration for this EventSource bool hasConfigForAppInsightsDataSource = eventSources.Any(config => AppInsightsDataEventSource.Equals(config.ProviderName, StringComparison.Ordinal) || AppInsightsDataEventSource.Equals(config.DisabledProviderNamePrefix, StringComparison.Ordinal)); if (!hasConfigForAppInsightsDataSource) { eventSources.Add(new EventSourceConfiguration() { DisabledProviderNamePrefix = AppInsightsDataEventSource }); } this.EventSources = eventSources; bool haveDisabledSources = this.EventSources.Any(config => !string.IsNullOrWhiteSpace(config.DisabledProviderNamePrefix)); if (haveDisabledSources) { this.disabledSources = new ConcurrentDictionary <string, bool>(); this.OnEventWrittenImpl = BroadcastEventIfSourceNotDisabled; } else { this.OnEventWrittenImpl = BroadcastEvent; } // Make sure the constructor has run to completion before enabling any sources. this.initialization = Task.Run(() => { this.constructed = true; EnableInitialSources(); }); }
internal static IConfigurationRoot ApplyFabricConfigurationOverrides(this IConfigurationRoot configurationRoot, IHealthReporter healthReporter) { Debug.Assert(configurationRoot != null); Debug.Assert(healthReporter != null); Regex fabricValueReferenceRegex = new Regex(FabricConfigurationValueReference, RegexOptions.CultureInvariant, TimeSpan.FromMilliseconds(100)); // Use ToList() to ensure that configuration is fully enumerated before starting to modify it. foreach (var kvp in configurationRoot.AsEnumerable().ToList()) { if (kvp.Value == null) { continue; } try { Match valueReferenceMatch = fabricValueReferenceRegex.Match(kvp.Value); if (valueReferenceMatch.Success) { string valueReferencePath = ConfigurationPath.Combine(valueReferenceMatch.Groups["section"].Value, valueReferenceMatch.Groups["name"].Value); string newValue = configurationRoot[valueReferencePath]; if (string.IsNullOrEmpty(newValue)) { healthReporter.ReportWarning( $"Configuration value reference '{kvp.Value}' was encountered but no corresponding configuration value was found using path '{valueReferencePath}'", EventFlowContextIdentifiers.Configuration); } else { configurationRoot[kvp.Key] = newValue; } } } catch (RegexMatchTimeoutException) { healthReporter.ReportWarning( $"Configuration entry with key '{kvp.Key}' and value '{kvp.Value}' could not be checked if it represents a configuration value reference--a timeout occurred when the value was being parsed.", EventFlowContextIdentifiers.Configuration); continue; } } return(configurationRoot); }
private EventData ToEventData(LogEvent logEvent) { EventData eventData = new EventData { ProviderName = nameof(SerilogInput), Timestamp = logEvent.Timestamp, Level = ToLogLevel[logEvent.Level], Keywords = 0 }; var payload = eventData.Payload; if (logEvent.Exception != null) { payload["Exception"] = logEvent.Exception; } // Inability to render the message, or any other LogEvent property, should not stop us from sending the event down the pipeline try { payload["Message"] = logEvent.RenderMessage(); } catch (Exception e) { healthReporter.ReportWarning($"{nameof(SerilogInput)}: event message could not be rendered{Environment.NewLine}{e.ToString()}"); } foreach (var property in logEvent.Properties.Where(property => property.Value != null)) { try { payload[property.Key] = ToRawValue(property.Value); } catch (Exception e) { healthReporter.ReportWarning($"{nameof(SerilogInput)}: event property '{property.Key}' could not be rendered{Environment.NewLine}{e.ToString()}"); } } return(eventData); }
public void Activate() { if (this.isDisposed) { throw new ObjectDisposedException(nameof(EtwInput)); } if (this.session != null) { throw new InvalidOperationException($"{nameof(EtwInput)} has already been activated"); } if (this.Providers.Count() == 0) { healthReporter.ReportWarning($"{nameof(EtwInput)}: no providers configured", nameof(EtwInput)); return; } this.session = SessionFactory(); foreach (var providerConfiguration in this.Providers) { string validationError; if (!providerConfiguration.Validate(out validationError)) { this.healthReporter.ReportWarning($"{nameof(EtwInput)}: one of the providers is configured improperly. {validationError}", EventFlowContextIdentifiers.Configuration); continue; } if (providerConfiguration.ProviderGuid != Guid.Empty) { this.session.EnableProvider(providerConfiguration.ProviderGuid, providerConfiguration.Level, (ulong)providerConfiguration.Keywords); } else { this.session.EnableProvider(providerConfiguration.ProviderName, providerConfiguration.Level, (ulong)providerConfiguration.Keywords); } } // Also enable .NET Task Parallel Library hierarchical activity tracking this.session.EnableProvider(TplActivities.TplEventSourceGuid, TraceEventLevel.Always, TplActivities.TaskFlowActivityIdsKeyword); Task.Factory.StartNew(() => { try { this.session.Process((eventData) => this.subject.OnNext(eventData)); } catch (Exception e) { this.healthReporter.ReportProblem($"{nameof(EtwInput)}: ETW session has terminated unexpectedly and events are no longer collected. {e.ToString()}"); } }, TaskCreationOptions.LongRunning); }
public static void AddPayloadProperty(IDictionary <string, object> payload, string key, object value, IHealthReporter healthReporter, string context) { Requires.NotNull(payload, nameof(payload)); Requires.NotNull(key, nameof(key)); Requires.NotNull(healthReporter, nameof(healthReporter)); if (!payload.TryGetValue(key, out var existingValue)) { payload.Add(key, value); return; } else if ((existingValue?.Equals(value)).GetValueOrDefault(false)) { // Existing value with same key is equivalent to the input value // We can return immediately to avoid adding duplicate key/value into the payload return; } string newKey; int i = 1; //update property key till there is no such key in dict do { newKey = key + "_" + i.ToString("d"); i++; }while (payload.TryGetValue(newKey, out existingValue) && !(existingValue?.Equals(value)).GetValueOrDefault(false)); if (!payload.ContainsKey(newKey)) { payload.Add(newKey, value); healthReporter.ReportWarning($"The property with the key '{key}' already exist in the event payload. Value was added under key '{newKey}'", context); } else { healthReporter.ReportWarning($"The property with the key '{key}' already exist in the event payload with equivalent value under key '{newKey}'. Value was not re-added", context); return; } }
private void Initialize(IReadOnlyCollection <EventSourceConfiguration> eventSources, IHealthReporter healthReporter) { this.healthReporter = healthReporter; this.subject = new EventFlowSubject <EventData>(); this.EventSources = eventSources; if (this.EventSources.Count() == 0) { healthReporter.ReportWarning($"{nameof(EventSourceInput)}: no event sources configured", nameof(EventSourceInput)); } // Make sure the constructor has run to completion before enabling any sources. this.initialization = Task.Run(() => { this.constructed = true; EnableInitialSources(); }); }
private void Initialize(IReadOnlyCollection <EventSourceConfiguration> eventSources, IHealthReporter healthReporter) { this.healthReporter = healthReporter; this.subject = new EventFlowSubject <EventData>(); this.EventSources = eventSources; if (this.EventSources.Count() == 0) { healthReporter.ReportWarning($"{nameof(EventSourceInput)}: no event sources configured", nameof(EventSourceInput)); return; } lock (this) // See OnEventSourceCreated() for explanation why we are locking on 'this' here. { EnableInitialSources(); this.constructed = true; } }
private void Initialize(List <EventSourceConfiguration> eventSources, IHealthReporter healthReporter) { this.healthReporter = healthReporter; this.subject = new EventFlowSubject <EventData>(); if (eventSources.Count() == 0) { healthReporter.ReportWarning($"{nameof(EventSourceInput)}: no event sources configured", EventFlowContextIdentifiers.Configuration); } var invalidConfigurationItems = new List <EventSourceConfiguration>(); foreach (var eventSourceConfiguration in eventSources) { if (!eventSourceConfiguration.Validate()) { healthReporter.ReportProblem($"{nameof(EventSourceInput)}: configuration for one of the sources is invalid", EventFlowContextIdentifiers.Configuration); invalidConfigurationItems.Add(eventSourceConfiguration); } } // eventSources is a collection created by us, so we can modify it as necessary eventSources.RemoveAll(config => invalidConfigurationItems.Contains(config)); this.EventSources = eventSources; bool haveDisabledSources = this.EventSources.Any(config => !string.IsNullOrWhiteSpace(config.DisabledProviderNamePrefix)); if (haveDisabledSources) { this.disabledSources = new ConcurrentDictionary <string, bool>(); this.OnEventWrittenImpl = BroadcastEventIfSourceNotDisabled; } else { this.OnEventWrittenImpl = BroadcastEvent; } // Make sure the constructor has run to completion before enabling any sources. this.initialization = Task.Run(() => { this.constructed = true; EnableInitialSources(); }); }
private TypeMappingDescriptor <object> applyTypeMapping(TypeMappingDescriptor <object> tm) { return(tm.Properties(pd => { PropertiesDescriptor <object> properties = pd; foreach (var propMapping in configuration.Mappings.Properties) { string propertyType = propMapping.Value.Type; string propertyName = propMapping.Key; if (!typeToPropertiesDesctiptorFunc.ContainsKey(propertyType)) { string errorMessage = $"{nameof(ElasticSearchOutput)}: {propertyName} property mapping could not be set because configured type ({propertyType}) is not supported."; healthReporter.ReportWarning(errorMessage, EventFlowContextIdentifiers.Output); } else { properties = typeToPropertiesDesctiptorFunc[propertyType](properties, propertyName); } } return properties; })); }
private EventData ToEventData(LogEventInfo logEvent) { var providerName = string.Empty; try { providerName = ProviderName?.Render(logEvent); } catch (Exception ex) { NLog.Common.InternalLogger.Warn(ex, NLogTargetTypeName + "(Name={0}): Failed to render event providername", Name); _healthReporter.ReportWarning($"{nameof(NLogInput)}: event providername could not be rendered{Environment.NewLine}{ex.ToString()}"); } if (string.IsNullOrEmpty(providerName)) { providerName = string.IsNullOrEmpty(logEvent.LoggerName) ? nameof(NLogInput) : logEvent.LoggerName; } EventData eventData = new EventData { ProviderName = providerName, Timestamp = logEvent.TimeStamp.ToUniversalTime(), Level = ToLogLevel[logEvent.Level], Keywords = 0, }; var payload = eventData.Payload; // Prefer the built-in `Message` and `Exception` properties by adding them to the payload // first. If other attached data items have conflicting names, they will be added as // `Message_1` and so-on. if (logEvent.Exception != null) { if (logEvent.Level.Ordinal >= NLog.LogLevel.Error.Ordinal) { EventMetadata eventMetadata = new EventMetadata(ExceptionData.ExceptionMetadataKind); eventMetadata.Properties.Add(ExceptionData.ExceptionPropertyMoniker, "Exception"); eventData.SetMetadata(eventMetadata); } eventData.AddPayloadProperty("Exception", logEvent.Exception, _healthReporter, nameof(NLogInput)); } // Inability to render the message, or any other LogEvent property, should not stop us from sending the event down the pipeline try { eventData.AddPayloadProperty("Message", RenderLogEvent(Layout, logEvent), _healthReporter, nameof(NLogInput)); } catch (Exception ex) { NLog.Common.InternalLogger.Warn(ex, NLogTargetTypeName + "(Name={0}): Failed to render event message", Name); _healthReporter.ReportWarning($"{nameof(NLogInput)}: event message could not be rendered{Environment.NewLine}{ex.ToString()}"); eventData.AddPayloadProperty("Message", logEvent.FormattedMessage, _healthReporter, nameof(NLogInput)); } if (ContextProperties.Count > 0) { // Include fixed properties like ThreadId, HostName, MessageTemplate, etc. CaptureTargetContextEventData(logEvent, eventData); } if (IncludeEventProperties && logEvent.HasProperties) { // Include properties coming from LogEvent CaptureLogEventPropertiesEventData(logEvent.Properties, eventData); } if (IncludeMdc || IncludeMdlc) { // Include any random bonus information CaptureLogEventContextProperties(GetContextProperties(logEvent), eventData); } return(eventData); }
public static void ReportThrottling(this IHealthReporter healthReporter) { healthReporter.ReportWarning("An event was dropped from the diagnostic pipeline because there was not enough capacity", EventFlowContextIdentifiers.Throttling); }
private static void ReportInvalidPipelineConfiguration(IHealthReporter healthReporter) { var errMsg = $"{nameof(DiagnosticPipelineFactory)}: pipeline settings configuration section is invalid--will use default settings for the diagnostic pipeline"; healthReporter.ReportWarning(errMsg); }
private static void CreateItemFactories( IConfiguration configuration, IHealthReporter healthReporter, out IDictionary <string, string> inputFactories, out IDictionary <string, string> outputFactories, out IDictionary <string, string> filterFactories) { Debug.Assert(configuration != null); Debug.Assert(healthReporter != null); inputFactories = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); inputFactories["EventSource"] = "Microsoft.Diagnostics.EventFlow.Inputs.EventSourceInputFactory, Microsoft.Diagnostics.EventFlow.Inputs.EventSource"; inputFactories["PerformanceCounter"] = "Microsoft.Diagnostics.EventFlow.Inputs.PerformanceCounterInputFactory, Microsoft.Diagnostics.EventFlow.Inputs.PerformanceCounter"; inputFactories["Trace"] = "Microsoft.Diagnostics.EventFlow.Inputs.TraceInputFactory, Microsoft.Diagnostics.EventFlow.Inputs.Trace"; inputFactories["Serilog"] = "Microsoft.Diagnostics.EventFlow.Inputs.SerilogInputFactory, Microsoft.Diagnostics.EventFlow.Inputs.Serilog"; inputFactories["Microsoft.Extensions.Logging"] = "Microsoft.Diagnostics.EventFlow.Inputs.LoggerInputFactory, Microsoft.Diagnostics.EventFlow.Inputs.MicrosoftLogging"; inputFactories["ETW"] = "Microsoft.Diagnostics.EventFlow.Inputs.EtwInputFactory, Microsoft.Diagnostics.EventFlow.Inputs.Etw"; inputFactories["ApplicationInsights"] = "Microsoft.Diagnostics.EventFlow.Inputs.ApplicationInsightsInputFactory, Microsoft.Diagnostics.EventFlow.Inputs.ApplicationInsights"; inputFactories["Log4net"] = "Microsoft.Diagnostics.EventFlow.Inputs.Log4netFactory, Microsoft.Diagnostics.EventFlow.Inputs.Log4net"; inputFactories["NLog"] = "Microsoft.Diagnostics.EventFlow.Inputs.NLogInputFactory, Microsoft.Diagnostics.EventFlow.Inputs.NLog"; inputFactories["DiagnosticSource"] = "Microsoft.Diagnostics.EventFlow.Inputs.DiagnosticSource.DiagnosticSourceInputFactory, Microsoft.Diagnostics.EventFlow.Inputs.DiagnosticSource"; outputFactories = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); outputFactories["ApplicationInsights"] = "Microsoft.Diagnostics.EventFlow.Outputs.ApplicationInsightsOutputFactory, Microsoft.Diagnostics.EventFlow.Outputs.ApplicationInsights"; outputFactories["StdOutput"] = "Microsoft.Diagnostics.EventFlow.Outputs.StdOutputFactory, Microsoft.Diagnostics.EventFlow.Outputs.StdOutput"; outputFactories["EventHub"] = "Microsoft.Diagnostics.EventFlow.Outputs.EventHubOutputFactory, Microsoft.Diagnostics.EventFlow.Outputs.EventHub"; outputFactories["ElasticSearch"] = "Microsoft.Diagnostics.EventFlow.Outputs.ElasticSearchOutputFactory, Microsoft.Diagnostics.EventFlow.Outputs.ElasticSearch"; outputFactories["OmsOutput"] = "Microsoft.Diagnostics.EventFlow.Outputs.OmsOutputFactory, Microsoft.Diagnostics.EventFlow.Outputs.Oms"; outputFactories["Http"] = "Microsoft.Diagnostics.EventFlow.Outputs.HttpOutputFactory, Microsoft.Diagnostics.EventFlow.Outputs.HttpOutput"; filterFactories = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); filterFactories["metadata"] = "Microsoft.Diagnostics.EventFlow.Filters.EventMetadataFilterFactory, Microsoft.Diagnostics.EventFlow.Core"; filterFactories["drop"] = "Microsoft.Diagnostics.EventFlow.Filters.DropFilterFactory, Microsoft.Diagnostics.EventFlow.Core"; // read 3rd party plugins IConfiguration extensionsConfiguration = configuration.GetSection("extensions"); foreach (var extension in extensionsConfiguration.GetChildren()) { var extConfig = new ExtensionsConfiguration(); extension.Bind(extConfig); IDictionary <string, string> targetFactories = null; if (string.Equals(extConfig.Category, ExtensionCategories.InputFactory, StringComparison.OrdinalIgnoreCase)) { targetFactories = inputFactories; } else if (string.Equals(extConfig.Category, ExtensionCategories.OutputFactory, StringComparison.OrdinalIgnoreCase)) { targetFactories = outputFactories; } else if (string.Equals(extConfig.Category, ExtensionCategories.FilterFactory, StringComparison.OrdinalIgnoreCase)) { targetFactories = filterFactories; } else if (string.Equals(extConfig.Category, ExtensionCategories.HealthReporter, StringComparison.OrdinalIgnoreCase)) { // Health reporter should have been created earlier, so skip continue; } else { healthReporter.ReportWarning($"Unrecognized extension category: {extConfig.Category}"); continue; } if (!string.IsNullOrEmpty(extConfig.Type) && !string.IsNullOrEmpty(extConfig.QualifiedTypeName)) { targetFactories[extConfig.Type] = extConfig.QualifiedTypeName; } else { healthReporter.ReportWarning($"Invalid extension configuration is skipped. Category: {extConfig.Category}, Type: {extConfig.Type}, QualifiedTypeName: {extConfig.QualifiedTypeName}"); } } }
public async Task SendEventsAsync(IReadOnlyCollection <EventData> events, long transmissionSequenceNumber, CancellationToken cancellationToken) { // Use a "Supressed" transaction scope to not be affected by callers rolling backs using (var scope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) { try { using (SqlConnection connection = new SqlConnection(ConnectionSettings.ConnectionString)) { await connection.OpenAsync(cancellationToken).ConfigureAwait(false); using (SqlCommand command = CreateCommand(connection)) { var transactionName = String.Concat(nameof(SqlTableOutput), transmissionSequenceNumber); using (command.Transaction = connection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted, transactionName)) { try { var inserted = 0; command.Prepare(); #if DEBUG Console.WriteLine($"{nameof(SqlTableOutput)}: will try to insert #{events.Count} events into SQL Server database"); #endif // Execute insert for each row in table foreach (var row in CreateEventsDataTable(events)) { foreach (var col in row) { command.Parameters[String.Concat(PARM_PREFIX, col.Key)].Value = col.Value ?? DBNull.Value; } if (cancellationToken.IsCancellationRequested) { return; } var result = await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); command.Transaction.Save(transactionName); inserted += result; } command.Transaction.Commit(); _healthReporter.ReportHealthy($"{nameof(SqlTableOutput)}: #{inserted} events written to SQL Server database", EventFlowContextIdentifiers.Output); #if DEBUG Console.WriteLine($"{nameof(SqlTableOutput)}: #{inserted} events written to SQL Server database"); #endif } catch (Exception sqlex) { _healthReporter.ReportWarning($"{nameof(SqlTableOutput)}: error writing data to SQL database. {sqlex.Message}", EventFlowContextIdentifiers.Output); #if DEBUG Console.WriteLine($"{nameof(SqlTableOutput)}: error writing data to SQL database. {sqlex.Message}"); Console.WriteLine(sqlex.StackTrace); #endif try { command.Transaction.Rollback(transactionName); connection.Close(); } catch (Exception rex) { _healthReporter.ReportWarning($"{nameof(SqlTableOutput)}: error rolling back. {rex.Message}", EventFlowContextIdentifiers.Output); #if DEBUG Console.WriteLine($"{nameof(SqlTableOutput)}: error rolling back. {rex.Message}"); Console.WriteLine(rex.StackTrace); #endif } throw; } } } connection.Close(); } // Not really needed, since the scope is "Suppressed" scope.Complete(); } catch (Exception ex) { _healthReporter.ReportProblem($"{nameof(SqlTableOutput)}: write of events data to SQL Server database has failed. {ex.Message}", EventFlowContextIdentifiers.Output); #if DEBUG Console.WriteLine($"{nameof(SqlTableOutput)}: write of events data to SQL Server database has failed. {ex.Message}"); Console.WriteLine(ex.StackTrace); #endif } } }