protected override Task OnProcessingErrorAsync(
                Exception exception,
                EventProcessorPartition partition,
                string operationDescription,
                CancellationToken cancellationToken)
            {
                try
                {
                    if (partition != null)
                    {
                        Console.Error.WriteLine(
                            $"Exception on partition { partition.PartitionId } while " +
                            $"performing { operationDescription }: {exception}");
                    }
                    else
                    {
                        Console.Error.WriteLine(
                            $"Exception while performing { operationDescription }: { exception }");
                    }
                }
                catch (Exception ex)
                {
                    // It is very important that you always guard against exceptions
                    // in your handler code; the processor does not have enough
                    // understanding of your code to determine the correct action to
                    // take.  Any exceptions from your handlers go uncaught by the
                    // processor and will NOT be handled in any way.

                    Console.WriteLine($"Exception while processing events: { ex }");
                }

                return(Task.CompletedTask);
            }
        public Dictionary <string, string> GetTriggerDetails(EventProcessorPartition context)
        {
            if (Events.Length == 0)
            {
                return(new Dictionary <string, string>());
            }

            string offset, enqueueTimeUtc, sequenceNumber;

            if (IsSingleDispatch)
            {
                offset         = Events[0].Offset.ToString(CultureInfo.InvariantCulture);
                enqueueTimeUtc = Events[0].EnqueuedTime.ToString("o", CultureInfo.InvariantCulture);
                sequenceNumber = Events[0].SequenceNumber.ToString(CultureInfo.InvariantCulture);
            }
            else
            {
                EventData first = Events[0];
                EventData last  = Events[Events.Length - 1];

                offset         = $"{first.Offset}-{last.Offset}";
                enqueueTimeUtc = $"{first.EnqueuedTime.ToString("o", CultureInfo.InvariantCulture)}-{last.EnqueuedTime.ToString("o", CultureInfo.InvariantCulture)}";
                sequenceNumber = $"{first.SequenceNumber}-{last.SequenceNumber}";
            }

            return(new Dictionary <string, string>()
            {
                { "PartionId", context.PartitionId },
                { "Offset", offset },
                { "EnqueueTimeUtc", enqueueTimeUtc },
                { "SequenceNumber", sequenceNumber },
                { "Count", Events.Length.ToString(CultureInfo.InvariantCulture) }
            });
        }
        public async Task EventProcesorCreatesScopeForEventProcessing()
        {
            using var cancellationSource = new CancellationTokenSource();
            cancellationSource.CancelAfter(TimeSpan.FromSeconds(30));

            using var listener = new ClientDiagnosticListener(EventDataInstrumentation.DiagnosticNamespace);

            var enqueuedTime = DateTimeOffset.UtcNow;
            var diagnosticId = 12;
            var partition    = new EventProcessorPartition {
                PartitionId = "123"
            };
            var fullyQualifiedNamespace = "namespace";
            var eventHubName            = "eventHub";
            var mockProcessor           = new Mock <EventProcessor <EventProcessorPartition> >(67, "consumerGroup", fullyQualifiedNamespace, eventHubName, Mock.Of <TokenCredential>(), default(EventProcessorOptions))
            {
                CallBase = true
            };

            var eventBatch = new List <EventData>
            {
                new EventData(new BinaryData(Array.Empty <byte>()), enqueuedTime: enqueuedTime),
                new EventData(new BinaryData(Array.Empty <byte>()), enqueuedTime: enqueuedTime)
            };

            eventBatch.ForEach(evt => evt.Properties.Add(DiagnosticProperty.DiagnosticIdAttribute, (++diagnosticId).ToString()));
            await mockProcessor.Object.ProcessEventBatchAsync(partition, eventBatch, false, cancellationSource.Token);

            // Validate the diagnostics.

            diagnosticId -= eventBatch.Count;

            var expectedTags = new List <KeyValuePair <string, string> >()
            {
                new KeyValuePair <string, string>(DiagnosticProperty.KindAttribute, DiagnosticProperty.ConsumerKind),
                new KeyValuePair <string, string>(DiagnosticProperty.EventHubAttribute, eventHubName),
                new KeyValuePair <string, string>(DiagnosticProperty.EndpointAttribute, fullyQualifiedNamespace)
            };

            var scopes = listener.Scopes.ToList();

            Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The cancellation token should not have been signaled.");
            Assert.That(scopes.Select(scope => scope.Name), Has.All.EqualTo(DiagnosticProperty.EventProcessorProcessingActivityName), "The processing scopes should have the correct name.");

            for (var index = 0; index < eventBatch.Count; ++index)
            {
                var targetId = (++diagnosticId).ToString();
                Assert.That(scopes.SelectMany(scope => scope.Links), Has.One.EqualTo(targetId), $"There should have been a link for the diagnostic identifier: { targetId }");
            }

            foreach (var scope in scopes)
            {
                Assert.That(expectedTags, Is.SubsetOf(scope.Activity.Tags.ToList()));
            }
        }
        public async Task EventProcesorAddsAttributesToLinkedActivitiesForEventProcessing()
        {
            using var cancellationSource = new CancellationTokenSource();
            cancellationSource.CancelAfter(TimeSpan.FromSeconds(30));

            using var listener = new ClientDiagnosticListener(EventDataInstrumentation.DiagnosticNamespace);

            var enqueuedTime = DateTimeOffset.UtcNow;
            var diagnosticId = "OMGHAI2U!";
            var eventBatch   = new List <EventData> {
                new EventData(new BinaryData(Array.Empty <byte>()), enqueuedTime: enqueuedTime)
            };
            var partition = new EventProcessorPartition {
                PartitionId = "123"
            };
            var fullyQualifiedNamespace = "namespace";
            var eventHubName            = "eventHub";
            var mockProcessor           = new Mock <EventProcessor <EventProcessorPartition> >(67, "consumerGroup", fullyQualifiedNamespace, eventHubName, Mock.Of <TokenCredential>(), default(EventProcessorOptions))
            {
                CallBase = true
            };

            eventBatch.ForEach(evt => evt.Properties.Add(DiagnosticProperty.DiagnosticIdAttribute, diagnosticId));
            await mockProcessor.Object.ProcessEventBatchAsync(partition, eventBatch, false, cancellationSource.Token);

            // Validate the diagnostics.

            Assert.That(cancellationSource.IsCancellationRequested, Is.False, "The cancellation token should not have been signaled.");

            var processingScope = listener.Scopes.SingleOrDefault(scope => scope.Name == DiagnosticProperty.EventProcessorProcessingActivityName);

            Assert.That(processingScope, Is.Not.Null, "There should have been a single scope present for the processing activity.");

            var linkedActivity = processingScope.LinkedActivities.SingleOrDefault(a => a.ParentId == diagnosticId);

            Assert.That(linkedActivity, Is.Not.Null, "There should have been a single activity linked to the diagnostic identifier.");

            var expectedTags = new List <KeyValuePair <string, string> >()
            {
                new KeyValuePair <string, string>(DiagnosticProperty.EnqueuedTimeAttribute, enqueuedTime.ToUnixTimeMilliseconds().ToString())
            };

            var tags = linkedActivity.Tags.ToList();

            Assert.That(tags, Is.EquivalentTo(expectedTags), "The activity should have been tagged appropriately.");
        }
            protected async override Task OnProcessingEventBatchAsync(
                IEnumerable <EventData> events,
                EventProcessorPartition partition,
                CancellationToken cancellationToken)
            {
                EventData lastEvent = null;

                try
                {
                    Console.WriteLine($"Received events for partition { partition.PartitionId }");

                    foreach (var currentEvent in events)
                    {
                        Console.WriteLine($"Event: { currentEvent.EventBody }");
                        lastEvent = currentEvent;
                    }

                    if (lastEvent != null)
                    {
                        await CheckpointAsync(
                            partition,
                            lastEvent,
                            cancellationToken)
                        .ConfigureAwait(false);
                    }
                }
                catch (Exception ex)
                {
                    // It is very important that you always guard against exceptions in
                    // your handler code; the processor does not have enough
                    // understanding of your code to determine the correct action to take.
                    // Any exceptions from your handlers go uncaught by the processor and
                    // will NOT be redirected to the error handler.

                    Console.WriteLine($"Exception while processing events: { ex }");
                }
            }