// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics; using OpenTelemetry.Internal; namespace OpenTelemetry; /// /// Represents a chain of s. /// /// The type of object to be processed. public class CompositeProcessor : BaseProcessor { internal readonly DoublyLinkedListNode Head; private DoublyLinkedListNode tail; private bool disposed; /// /// Initializes a new instance of the class. /// /// Processors to add to the composite processor chain. public CompositeProcessor(IEnumerable> processors) { Guard.ThrowIfNull(processors); #pragma warning disable CA1062 // Validate arguments of public methods - needed for netstandard2.1 using var iter = processors.GetEnumerator(); #pragma warning restore CA1062 // Validate arguments of public methods - needed for netstandard2.1 if (!iter.MoveNext()) { throw new ArgumentException($"'{iter}' is null or empty", nameof(processors)); } this.Head = new DoublyLinkedListNode(iter.Current); this.tail = this.Head; while (iter.MoveNext()) { this.AddProcessor(iter.Current); } } /// /// Adds a processor to the composite processor chain. /// /// . /// The current instance to support call chaining. public CompositeProcessor AddProcessor(BaseProcessor processor) { Guard.ThrowIfNull(processor); var node = new DoublyLinkedListNode(processor) { Previous = this.tail, }; this.tail.Next = node; this.tail = node; return this; } /// public override void OnEnd(T data) { for (var cur = this.Head; cur != null; cur = cur.Next) { cur.Value.OnEnd(data); } } /// public override void OnStart(T data) { for (var cur = this.Head; cur != null; cur = cur.Next) { cur.Value.OnStart(data); } } internal override void SetParentProvider(BaseProvider parentProvider) { base.SetParentProvider(parentProvider); for (var cur = this.Head; cur != null; cur = cur.Next) { cur.Value.SetParentProvider(parentProvider); } } internal IReadOnlyList> ToReadOnlyList() { var list = new List>(); for (var cur = this.Head; cur != null; cur = cur.Next) { list.Add(cur.Value); } return list; } /// protected override bool OnForceFlush(int timeoutMilliseconds) { var result = true; var sw = timeoutMilliseconds == Timeout.Infinite ? null : Stopwatch.StartNew(); for (var cur = this.Head; cur != null; cur = cur.Next) { if (sw == null) { result = cur.Value.ForceFlush() && result; } else { var timeout = timeoutMilliseconds - sw.ElapsedMilliseconds; // notify all the processors, even if we run overtime result = cur.Value.ForceFlush((int)Math.Max(timeout, 0)) && result; } } return result; } /// protected override bool OnShutdown(int timeoutMilliseconds) { var result = true; var sw = timeoutMilliseconds == Timeout.Infinite ? null : Stopwatch.StartNew(); for (var cur = this.Head; cur != null; cur = cur.Next) { if (sw == null) { result = cur.Value.Shutdown() && result; } else { var timeout = timeoutMilliseconds - sw.ElapsedMilliseconds; // notify all the processors, even if we run overtime result = cur.Value.Shutdown((int)Math.Max(timeout, 0)) && result; } } return result; } /// protected override void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { for (var cur = this.Head; cur != null; cur = cur.Next) { try { cur.Value.Dispose(); } catch (Exception ex) { OpenTelemetrySdkEventSource.Log.SpanProcessorException(nameof(this.Dispose), ex); } } } this.disposed = true; } base.Dispose(disposing); } internal sealed class DoublyLinkedListNode { public readonly BaseProcessor Value; public DoublyLinkedListNode(BaseProcessor value) { this.Value = value; } public DoublyLinkedListNode? Previous { get; set; } public DoublyLinkedListNode? Next { get; set; } } }