예제 #1
0
        // The Initialize method is not thread-safe. Please only call this on one thread and do so before the pipeline starts sending
        // data to this output
        private void Initialize(EventHubOutputConfiguration configuration)
        {
            Debug.Assert(configuration != null);
            Debug.Assert(this.healthReporter != null);

            if (string.IsNullOrWhiteSpace(configuration.ConnectionString))
            {
                var errorMessage = $"{nameof(EventHubOutput)}: '{nameof(EventHubOutputConfiguration.ConnectionString)}' configuration parameter must be set to a valid connection string";
                healthReporter.ReportProblem(errorMessage, EventFlowContextIdentifiers.Configuration);
                throw new Exception(errorMessage);
            }

            EventHubsConnectionStringBuilder connStringBuilder = new EventHubsConnectionStringBuilder(configuration.ConnectionString);

            this.eventHubName = connStringBuilder.EntityPath ?? configuration.EventHubName;
            if (string.IsNullOrWhiteSpace(this.eventHubName))
            {
                var errorMessage = $"{nameof(EventHubOutput)}: Event Hub name must not be empty. It can be specified in the '{nameof(EventHubOutputConfiguration.ConnectionString)}' or '{nameof(EventHubOutputConfiguration.EventHubName)}' configuration parameter";

                healthReporter.ReportProblem(errorMessage, EventFlowContextIdentifiers.Configuration);
                throw new Exception(errorMessage);
            }
            connStringBuilder.EntityPath = this.eventHubName;

            this.clients = new EventHubClient[ConcurrentConnections];
            for (uint i = 0; i < this.clients.Length; i++)
            {
                this.clients[i] = EventHubClient.CreateFromConnectionString(connStringBuilder.ToString());
            }
        }
예제 #2
0
        public EventHubOutput(EventHubOutputConfiguration eventHubOutputConfiguration, IHealthReporter healthReporter)
        {
            Requires.NotNull(eventHubOutputConfiguration, nameof(eventHubOutputConfiguration));
            Requires.NotNull(healthReporter, nameof(healthReporter));

            this.healthReporter = healthReporter;
            Initialize(eventHubOutputConfiguration);
        }
예제 #3
0
        public EventHubOutput(
            EventHubOutputConfiguration eventHubOutputConfiguration,
            IHealthReporter healthReporter,
            Func <string, IEventHubClient> eventHubClientFactory = null)
        {
            Requires.NotNull(eventHubOutputConfiguration, nameof(eventHubOutputConfiguration));
            Requires.NotNull(healthReporter, nameof(healthReporter));

            this.healthReporter        = healthReporter;
            this.eventHubClientFactory = eventHubClientFactory ?? this.CreateEventHubClient;
            this.outputConfiguration   = eventHubOutputConfiguration;
            Initialize();
        }
예제 #4
0
        private IEventHubClient CreateEventHubClient(EventHubOutputConfiguration _)
        {
            Debug.Assert(this.outputConfiguration != null);

            if (this.outputConfiguration.UseAzureIdentity)
            {
                this.eventHubName = this.outputConfiguration.EventHubName;
                ensureEventHubName();

                if (string.IsNullOrWhiteSpace(this.outputConfiguration.FullyQualifiedNamespace))
                {
                    var emptyNamespaceMsg = $"{nameof(EventHubOutput)}: Event Hub namespace must not be empty when using Azure Identity. It can be specified in the '{nameof(EventHubOutputConfiguration.FullyQualifiedNamespace)}' configuration parameter";
                    healthReporter.ReportProblem(emptyNamespaceMsg, EventFlowContextIdentifiers.Configuration);
                    throw new Exception(emptyNamespaceMsg);
                }

                TokenCredential azureTokenCredential = this.outputConfiguration.AzureTokenCredential ?? new DefaultAzureCredential();

                return(new EventHubClientImpl(
                           new EventHubProducerClient(this.outputConfiguration.FullyQualifiedNamespace, this.eventHubName, azureTokenCredential)
                           ));
            }

            if (!string.IsNullOrWhiteSpace(this.outputConfiguration.ConnectionString))
            {
                var connString = EventHubsConnectionStringProperties.Parse(this.outputConfiguration.ConnectionString);
                this.eventHubName = connString.EventHubName ?? this.outputConfiguration.EventHubName;
                ensureEventHubName();

                return(new EventHubClientImpl(
                           new EventHubProducerClient(this.outputConfiguration.ConnectionString, this.eventHubName)
                           ));
            }

            var invalidConfigMsg =
                $"Invalid {nameof(EventHubOutput)} configuration encountered: '{nameof(EventHubOutputConfiguration.ConnectionString)}' value is empty and '{nameof(EventHubOutputConfiguration.UseAzureIdentity)}' is set to false. " +
                $"You need to specify either '{nameof(EventHubOutputConfiguration.ConnectionString)}' to EventHub or set '{nameof(EventHubOutputConfiguration.UseAzureIdentity)}' flag.";

            healthReporter.ReportProblem(invalidConfigMsg, EventFlowContextIdentifiers.Configuration);
            throw new Exception(invalidConfigMsg);

            void ensureEventHubName()
            {
                if (string.IsNullOrWhiteSpace(this.eventHubName))
                {
                    var emptyEventHubNameMsg = $"{nameof(EventHubOutput)}: Event Hub name must not be empty. It can be specified in the '{nameof(EventHubOutputConfiguration.ConnectionString)}' or '{nameof(EventHubOutputConfiguration.EventHubName)}' configuration parameter";
                    healthReporter.ReportProblem(emptyEventHubNameMsg);
                    throw new Exception(emptyEventHubNameMsg);
                }
            }
        }
예제 #5
0
        private async Task SendsManyItemsToPartitionedEventHub(int itemCount)
        {
            var client         = new Mock <IEventHubClient>();
            var healthReporter = new Mock <IHealthReporter>();
            var configuration  = new EventHubOutputConfiguration();

            configuration.ConnectionString     = "Connection string";
            configuration.EventHubName         = "foo";
            configuration.PartitionKeyProperty = "PartitionByProperty";

            EventHubOutput eho = new EventHubOutput(configuration, healthReporter.Object, connectionString => client.Object);

            //send `itemCount` EventData items with payload of 55Kb+ each and targeting PartitionKey "partition3"
            string payload = new string('a', 55000);
            await eho.SendEventsAsync(Enumerable.Range(0, itemCount)
                                      .Select(r =>
            {
                var eventData = new EventData();
                eventData.ProviderName = "TestProvider";
                eventData.Timestamp = DateTimeOffset.UtcNow;
                eventData.Level = LogLevel.Warning;
                eventData.Payload.Add("IntProperty", r);
                eventData.Payload.Add("StringProperty", payload);
                eventData.Payload.Add("PartitionByProperty", "partition3");
                return(eventData);
            })
                                      .ToList(), 17, CancellationToken.None);

            Func <IEnumerable <MessagingEventData>, bool> verifyBatch = batch =>
            {
                if (batch.Count() != 4)
                {
                    return(false);
                }
                return(true);
            };

            //the individual messages are 55Kb in size each, so a maximum of 4 messages total byte size fits within EventHubMessageSizeLimit limits.
            //so we expect `itemCount` / 4 batches executed against EventHubClient.SendAsync()
            client.Verify(c => c.SendAsync(It.Is <IEnumerable <MessagingEventData> >(b => verifyBatch(b)), "partition3"), Times.Exactly(itemCount / 4));
            healthReporter.Verify(hr => hr.ReportWarning(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
            healthReporter.Verify(hr => hr.ReportProblem(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
        }
        public async Task SendsDataToEventHub()
        {
            var client         = new Mock <IEventHubClient>();
            var healthReporter = new Mock <IHealthReporter>();
            var configuration  = new EventHubOutputConfiguration();

            configuration.ConnectionString = "Connection string";
            configuration.EventHubName     = "foo";

            EventData e = new EventData();

            e.ProviderName = "TestProvider";
            e.Timestamp    = DateTimeOffset.UtcNow;
            e.Level        = LogLevel.Warning;
            e.Payload.Add("IntProperty", 42);
            e.Payload.Add("StringProperty", "perfection");

            EventHubOutput eho = new EventHubOutput(configuration, healthReporter.Object, connectionString => client.Object);
            await eho.SendEventsAsync(new EventData[] { e, }, 17, CancellationToken.None);

            Func <IEnumerable <MessagingEventData>, bool> verifyBatch = batch =>
            {
                if (batch.Count() != 1)
                {
                    return(false);
                }

                var data       = batch.First();
                var dataBytes  = data.EventBody.ToArray();
                var bodyString = Encoding.UTF8.GetString(dataBytes, (int)data.Offset, dataBytes.Length);
                var recordSet  = JObject.Parse(bodyString);
                var message    = recordSet["records"][0];

                return((string)message["level"] == "Warning" &&
                       (string)message["properties"]["ProviderName"] == "TestProvider" &&
                       (int)message["properties"]["IntProperty"] == 42 &&
                       (string)message["properties"]["StringProperty"] == "perfection");
            };

            client.Verify(c => c.SendAsync(It.Is <IEnumerable <MessagingEventData> >(b => verifyBatch(b))), Times.Once);
            healthReporter.Verify(hr => hr.ReportWarning(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
            healthReporter.Verify(hr => hr.ReportProblem(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
        }
예제 #7
0
        public EventHubOutput(IConfiguration configuration, IHealthReporter healthReporter)
        {
            Requires.NotNull(configuration, nameof(configuration));
            Requires.NotNull(healthReporter, nameof(healthReporter));

            this.healthReporter = healthReporter;
            var eventHubOutputConfiguration = new EventHubOutputConfiguration();

            try
            {
                configuration.Bind(eventHubOutputConfiguration);
            }
            catch
            {
                healthReporter.ReportProblem($"Invalid {nameof(EventHubOutput)} configuration encountered: '{configuration.ToString()}'",
                                             EventFlowContextIdentifiers.Configuration);
                throw;
            }

            Initialize(eventHubOutputConfiguration);
        }
예제 #8
0
        public async Task MethodInfoIsSerializedAsFullyQualifiedName()
        {
            var client         = new Mock <IEventHubClient>();
            var healthReporter = new Mock <IHealthReporter>();
            var configuration  = new EventHubOutputConfiguration();

            configuration.ConnectionString = "Connection string";
            configuration.EventHubName     = "foo";

            EventData e = new EventData();

            e.ProviderName = "TestProvider";
            e.Timestamp    = DateTimeOffset.UtcNow;
            e.Level        = LogLevel.Warning;
            e.Payload.Add("Method", typeof(EventHubOutputTests).GetMethod(nameof(MethodInfoIsSerializedAsFullyQualifiedName)));

            EventHubOutput eho = new EventHubOutput(configuration, healthReporter.Object, connectionString => client.Object);
            await eho.SendEventsAsync(new EventData[] { e, }, 17, CancellationToken.None);

            Func <IEnumerable <MessagingEventData>, bool> verifyBatch = batch =>
            {
                if (batch.Count() != 1)
                {
                    return(false);
                }

                var data       = batch.First();
                var bodyString = Encoding.UTF8.GetString(data.Body.Array, data.Body.Offset, data.Body.Count);
                var recordSet  = JObject.Parse(bodyString);
                var message    = recordSet["records"][0];

                return((string)message["level"] == "Warning" &&
                       (string)message["properties"]["ProviderName"] == "TestProvider" &&
                       (string)message["properties"]["Method"] == "Microsoft.Diagnostics.EventFlow.Outputs.Tests.EventHubOutputTests.MethodInfoIsSerializedAsFullyQualifiedName");
            };

            client.Verify(c => c.SendAsync(It.Is <IEnumerable <MessagingEventData> >(b => verifyBatch(b))), Times.Once);
            healthReporter.Verify(hr => hr.ReportWarning(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
            healthReporter.Verify(hr => hr.ReportProblem(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
        }
예제 #9
0
        // The Initialize method is not thread-safe. Please only call this on one thread and do so before the pipeline starts sending
        // data to this output
        private void Initialize(EventHubOutputConfiguration configuration)
        {
            Debug.Assert(configuration != null);
            Debug.Assert(this.healthReporter != null);

            if (string.IsNullOrWhiteSpace(configuration.ConnectionString))
            {
                var errorMessage = $"{nameof(EventHubOutput)}: '{nameof(EventHubOutputConfiguration.ConnectionString)}' configuration parameter must be set to a valid connection string";
                healthReporter.ReportProblem(errorMessage, EventFlowContextIdentifiers.Configuration);
                throw new Exception(errorMessage);
            }

            ServiceBusConnectionStringBuilder connStringBuilder = new ServiceBusConnectionStringBuilder(configuration.ConnectionString);

            connStringBuilder.TransportType = TransportType.Amqp;

            this.eventHubName = connStringBuilder.EntityPath ?? configuration.EventHubName;
            if (string.IsNullOrWhiteSpace(this.eventHubName))
            {
                var errorMessage = $"{nameof(EventHubOutput)}: Event Hub name must not be empty. It can be specified in the '{nameof(EventHubOutputConfiguration.ConnectionString)}' or '{nameof(EventHubOutputConfiguration.EventHubName)}' configuration parameter";

                healthReporter.ReportProblem(errorMessage, EventFlowContextIdentifiers.Configuration);
                throw new Exception(errorMessage);
            }

            this.connections = new EventHubConnection[ConcurrentConnections];

            // To create a MessageFactory, the connection string can't contain the EntityPath. So we set it to null here.
            connStringBuilder.EntityPath = null;
            for (uint i = 0; i < this.connections.Length; i++)
            {
                MessagingFactory factory = MessagingFactory.CreateFromConnectionString(connStringBuilder.ToString());
                this.connections[i] = new EventHubConnection();
                this.connections[i].MessagingFactory = factory;
                this.connections[i].HubClient        = factory.CreateEventHubClient(this.eventHubName);
            }
        }
예제 #10
0
        public async Task SendsDataToPartitionedEventHub()
        {
            var client         = new Mock <IEventHubClient>();
            var healthReporter = new Mock <IHealthReporter>();
            var configuration  = new EventHubOutputConfiguration();

            configuration.ConnectionString     = "Connection string";
            configuration.EventHubName         = "foo";
            configuration.PartitionKeyProperty = "PartitionByProperty";

            EventData e1 = new EventData();

            e1.ProviderName = "TestProvider";
            e1.Timestamp    = DateTimeOffset.UtcNow;
            e1.Level        = LogLevel.Warning;
            e1.Payload.Add("IntProperty", 23);
            e1.Payload.Add("StringProperty", "partition-perfection");
            e1.Payload.Add("PartitionByProperty", "partition1");

            EventData e2 = new EventData();

            e2.ProviderName = "TestProvider";
            e2.Timestamp    = DateTimeOffset.UtcNow;
            e2.Level        = LogLevel.Warning;
            e2.Payload.Add("IntProperty", 23);
            e2.Payload.Add("StringProperty", "partition-perfection");

            EventData e3 = new EventData();

            e3.ProviderName = "TestProvider";
            e3.Timestamp    = DateTimeOffset.UtcNow;
            e3.Level        = LogLevel.Warning;
            e3.Payload.Add("IntProperty", 23);
            e3.Payload.Add("StringProperty", new string('a', 150000));
            e3.Payload.Add("PartitionByProperty", "partition2");

            EventData e4 = new EventData();

            e4.ProviderName = "TestProvider";
            e4.Timestamp    = DateTimeOffset.UtcNow;
            e4.Level        = LogLevel.Warning;
            e4.Payload.Add("IntProperty", 23);
            e4.Payload.Add("StringProperty", new string('b', 150000));
            e4.Payload.Add("PartitionByProperty", "partition2");

            EventData e5 = new EventData();

            e5.ProviderName = "TestProvider";
            e5.Timestamp    = DateTimeOffset.UtcNow;
            e5.Level        = LogLevel.Warning;
            e5.Payload.Add("IntProperty", 23);
            e5.Payload.Add("StringProperty", new string('c', 150000));
            e5.Payload.Add("PartitionByProperty", "partition2");

            EventHubOutput eho = new EventHubOutput(configuration, healthReporter.Object, connectionString => client.Object);
            await eho.SendEventsAsync(new[] { e1, e2, e3, e4, e5 }, 17, CancellationToken.None);

            Func <IEnumerable <MessagingEventData>, bool> verifyBatch = batch =>
            {
                if (batch.Count() != 1)
                {
                    return(false);
                }
                return(true);
            };

            client.Verify(c => c.SendAsync(It.Is <IEnumerable <MessagingEventData> >(b => verifyBatch(b))), Times.Once);
            client.Verify(c => c.SendAsync(It.Is <IEnumerable <MessagingEventData> >(b => verifyBatch(b)), "partition1"), Times.Once);
            client.Verify(c => c.SendAsync(It.Is <IEnumerable <MessagingEventData> >(b => verifyBatch(b)), "partition2"), Times.Exactly(3));
            healthReporter.Verify(hr => hr.ReportWarning(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
            healthReporter.Verify(hr => hr.ReportProblem(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
        }
예제 #11
0
        public async Task UsesCustomJsonSerializerSettings()
        {
            var client         = new Mock <IEventHubClient>();
            var healthReporter = new Mock <IHealthReporter>();
            var configuration  = new EventHubOutputConfiguration();

            configuration.ConnectionString = "Connection string";
            configuration.EventHubName     = "foo";

            EventData e = new EventData();

            e.ProviderName = "TestProvider";
            e.Timestamp    = DateTimeOffset.UtcNow;
            e.Level        = LogLevel.Warning;
            e.Payload.Add("InfinityProperty", Double.PositiveInfinity);

            EventHubOutput eho = new EventHubOutput(configuration, healthReporter.Object, connectionString => client.Object);
            await eho.SendEventsAsync(new EventData[] { e, }, 17, CancellationToken.None);

            Func <IEnumerable <MessagingEventData>, bool> verifyBatch = batch =>
            {
                if (batch.Count() != 1)
                {
                    return(false);
                }

                var data       = batch.First();
                var bodyString = Encoding.UTF8.GetString(data.Body.Array, data.Body.Offset, data.Body.Count);
                var recordSet  = JObject.Parse(bodyString);
                var message    = recordSet["records"][0];

                return((string)message["level"] == "Warning" &&
                       (string)message["properties"]["ProviderName"] == "TestProvider"
                       // By default floating-point infinity values are converted to strings
                       && (string)message["properties"]["InfinityProperty"] == "Infinity");
            };

            client.Verify(c => c.SendAsync(It.Is <IEnumerable <MessagingEventData> >(b => verifyBatch(b))), Times.Once);
            healthReporter.Verify(hr => hr.ReportWarning(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
            healthReporter.Verify(hr => hr.ReportProblem(It.IsAny <string>(), It.IsAny <string>()), Times.Never);

            // Now verify changing serializer settings is effective
            eho.SerializerSettings.FloatFormatHandling = FloatFormatHandling.DefaultValue;
            client.ResetCalls();

            verifyBatch = batch =>
            {
                if (batch.Count() != 1)
                {
                    return(false);
                }

                var data       = batch.First();
                var bodyString = Encoding.UTF8.GetString(data.Body.Array, data.Body.Offset, data.Body.Count);
                var recordSet  = JObject.Parse(bodyString);
                var message    = recordSet["records"][0];

                return((string)message["level"] == "Warning" &&
                       (string)message["properties"]["ProviderName"] == "TestProvider" &&
                       (double)message["properties"].OfType <JProperty>().First(p => p.Name == "InfinityProperty").OfType <JValue>().First().Value == 0.0);
            };

            await eho.SendEventsAsync(new EventData[] { e, }, 18, CancellationToken.None);

            client.Verify(c => c.SendAsync(It.Is <IEnumerable <MessagingEventData> >(b => verifyBatch(b))), Times.Once);
            healthReporter.Verify(hr => hr.ReportWarning(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
            healthReporter.Verify(hr => hr.ReportProblem(It.IsAny <string>(), It.IsAny <string>()), Times.Never);
        }