示例#1
0
        public void End()
        {
            if (Duration.HasValue)
            {
                _logger.Trace()
                ?.Log("Ended {Span} (with Duration already set)." +
                      " Start time: {Time} (as timestamp: {Timestamp}), Duration: {Duration}ms",
                      this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp, Duration);
            }
            else
            {
                Assertion.IfEnabled?.That(!_isEnded,
                                          $"Span's Duration doesn't have value even though {nameof(End)} method was already called." +
                                          $" It contradicts the invariant enforced by {nameof(End)} method - Duration should have value when {nameof(End)} method exits" +
                                          $" and {nameof(_isEnded)} field is set to true only when {nameof(End)} method exits." +
                                          $" Context: this: {this}; {nameof(_isEnded)}: {_isEnded}");

                var endTimestamp = TimeUtils.TimestampNow();
                Duration = TimeUtils.DurationBetweenTimestamps(Timestamp, endTimestamp);
                _logger.Trace()
                ?.Log("Ended {Span}. Start time: {Time} (as timestamp: {Timestamp})," +
                      " End time: {Time} (as timestamp: {Timestamp}), Duration: {Duration}ms",
                      this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp,
                      TimeUtils.FormatTimestampForLog(endTimestamp), endTimestamp, Duration);
            }

            var isFirstEndCall = !_isEnded;

            _isEnded = true;
            if (IsSampled && isFirstEndCall)
            {
                _payloadSender.QueueSpan(this);
            }
        }
示例#2
0
        public Error(CapturedException capturedException, Transaction transaction, string parentId, IApmLogger loggerArg, Dictionary <string, Label> labels = null)
        {
            Timestamp = TimeUtils.TimestampNow();
            Id        = RandomGenerator.GenerateRandomBytesAsString(new byte[16]);

            Exception = capturedException;

            TraceId       = transaction.TraceId;
            TransactionId = transaction.Id;
            ParentId      = parentId;
            Transaction   = new TransactionData(transaction.IsSampled, transaction.Type);

            if (transaction.IsSampled)
            {
                Context = transaction.Context.DeepCopy();

                if (labels != null)
                {
                    foreach (var item in labels)
                    {
                        Context.InternalLabels.Value.InnerDictionary[item.Key] = item.Value;
                    }
                }
            }

            IApmLogger logger = loggerArg?.Scoped($"{nameof(Error)}.{Id}");

            logger.Trace()
            ?.Log("New Error instance created: {Error}. Time: {Time} (as timestamp: {Timestamp})",
                  this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
        }
示例#3
0
        internal Transaction(
            IApmLogger logger,
            string name,
            string type,
            Sampler sampler,
            DistributedTracingData distributedTracingData,
            IPayloadSender sender,
            IConfigurationReader configurationReader
            )
        {
            Timestamp = TimeUtils.TimestampNow();
            var idBytes = new byte[8];

            Id      = RandomGenerator.GenerateRandomBytesAsString(idBytes);
            _logger = logger?.Scoped($"{nameof(Transaction)}.{Id}");

            _sender = sender;
            _configurationReader = configurationReader;

            Name          = name;
            HasCustomName = false;
            Type          = type;

            var isSamplingFromDistributedTracingData = false;

            if (distributedTracingData == null)
            {
                var traceIdBytes = new byte[16];
                TraceId   = RandomGenerator.GenerateRandomBytesAsString(traceIdBytes);
                IsSampled = sampler.DecideIfToSample(idBytes);
            }
            else
            {
                TraceId   = distributedTracingData.TraceId;
                ParentId  = distributedTracingData.ParentId;
                IsSampled = distributedTracingData.FlagRecorded;
                isSamplingFromDistributedTracingData = true;
            }

            SpanCount = new SpanCount();

            if (isSamplingFromDistributedTracingData)
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on incoming distributed tracing data ({DistributedTracingData})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, distributedTracingData, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
            else
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on the given sampler ({Sampler})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, sampler, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
        }
示例#4
0
        public void End()
        {
            if (Duration.HasValue)
            {
                _logger.Trace()
                ?.Log("Ended {Span} (with Duration already set)." +
                      " Start time: {Time} (as timestamp: {Timestamp}), Duration: {Duration}ms",
                      this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp, Duration);
            }
            else
            {
                Assertion.IfEnabled?.That(!_isEnded,
                                          $"Span's Duration doesn't have value even though {nameof(End)} method was already called." +
                                          $" It contradicts the invariant enforced by {nameof(End)} method - Duration should have value when {nameof(End)} method exits" +
                                          $" and {nameof(_isEnded)} field is set to true only when {nameof(End)} method exits." +
                                          $" Context: this: {this}; {nameof(_isEnded)}: {_isEnded}");

                var endTimestamp = TimeUtils.TimestampNow();
                Duration = TimeUtils.DurationBetweenTimestamps(Timestamp, endTimestamp);
                _logger.Trace()
                ?.Log("Ended {Span}. Start time: {Time} (as timestamp: {Timestamp})," +
                      " End time: {Time} (as timestamp: {Timestamp}), Duration: {Duration}ms",
                      this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp,
                      TimeUtils.FormatTimestampForLog(endTimestamp), endTimestamp, Duration);
            }

            var isFirstEndCall = !_isEnded;

            _isEnded = true;

            if (ShouldBeSentToApmServer && isFirstEndCall)
            {
                try
                {
                    DeduceDestination();
                }
                catch (Exception e)
                {
                    _logger.Warning()?.LogException(e, "Failed deducing destination fields for span.");
                }

                // Spans are sent only for sampled transactions so it's only worth capturing stack trace for sampled spans
                // ReSharper disable once CompareOfFloatsByEqualityOperator
                if (ConfigSnapshot.StackTraceLimit != 0 && ConfigSnapshot.SpanFramesMinDurationInMilliseconds != 0 && RawStackTrace == null &&
                    (Duration >= ConfigSnapshot.SpanFramesMinDurationInMilliseconds ||
                     ConfigSnapshot.SpanFramesMinDurationInMilliseconds < 0))
                {
                    RawStackTrace = new StackTrace(true);
                }

                _payloadSender.QueueSpan(this);
            }

            if (isFirstEndCall)
            {
                _currentExecutionSegmentsContainer.CurrentSpan = _parentSpan;
            }
        }
示例#5
0
        public Span(
            string name,
            string type,
            string parentId,
            string traceId,
            Transaction enclosingTransaction,
            IPayloadSender payloadSender,
            IApmLogger logger,
            ICurrentExecutionSegmentsContainer currentExecutionSegmentsContainer,
            Span parentSpan = null,
            InstrumentationFlag instrumentationFlag = InstrumentationFlag.None,
            bool captureStackTraceOnStart           = false
            )
        {
            InstrumentationFlag = instrumentationFlag;
            Timestamp           = TimeUtils.TimestampNow();
            Id      = RandomGenerator.GenerateRandomBytesAsString(new byte[8]);
            _logger = logger?.Scoped($"{nameof(Span)}.{Id}");

            _payloadSender = payloadSender;
            _currentExecutionSegmentsContainer = currentExecutionSegmentsContainer;
            _parentSpan           = parentSpan;
            _enclosingTransaction = enclosingTransaction;
            Name = name;
            Type = type;

            ParentId = parentId;
            TraceId  = traceId;

            if (IsSampled)
            {
                // Started and dropped spans should be counted only for sampled transactions
                if (enclosingTransaction.SpanCount.IncrementTotal() > ConfigSnapshot.TransactionMaxSpans &&
                    ConfigSnapshot.TransactionMaxSpans >= 0)
                {
                    _isDropped = true;
                    enclosingTransaction.SpanCount.IncrementDropped();
                }
                else
                {
                    enclosingTransaction.SpanCount.IncrementStarted();

                    if (captureStackTraceOnStart)
                    {
                        _stackFrames = new EnhancedStackTrace(new StackTrace(true)).GetFrames();
                    }
                }
            }

            _currentExecutionSegmentsContainer.CurrentSpan = this;

            _logger.Trace()
            ?.Log("New Span instance created: {Span}. Start time: {Time} (as timestamp: {Timestamp}). Parent span: {Span}",
                  this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp, _parentSpan);
        }
示例#6
0
        public void End()
        {
            if (Duration.HasValue)
            {
                _logger.Trace()
                ?.Log("Ended {Span} (with Duration already set)." +
                      " Start time: {Time} (as timestamp: {Timestamp}), Duration: {Duration}ms",
                      this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp, Duration);
            }
            else
            {
                Assertion.IfEnabled?.That(!_isEnded,
                                          $"Span's Duration doesn't have value even though {nameof(End)} method was already called." +
                                          $" It contradicts the invariant enforced by {nameof(End)} method - Duration should have value when {nameof(End)} method exits" +
                                          $" and {nameof(_isEnded)} field is set to true only when {nameof(End)} method exits." +
                                          $" Context: this: {this}; {nameof(_isEnded)}: {_isEnded}");

                var endTimestamp = TimeUtils.TimestampNow();
                Duration = TimeUtils.DurationBetweenTimestamps(Timestamp, endTimestamp);
                _logger.Trace()
                ?.Log("Ended {Span}. Start time: {Time} (as timestamp: {Timestamp})," +
                      " End time: {Time} (as timestamp: {Timestamp}), Duration: {Duration}ms",
                      this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp,
                      TimeUtils.FormatTimestampForLog(endTimestamp), endTimestamp, Duration);
            }

            var isFirstEndCall = !_isEnded;

            _isEnded = true;

            if (!IsSampled || !isFirstEndCall)
            {
                return;
            }

            // Spans are sent only for sampled transactions so it's only worth capturing stack trace for sampled spans
            // ReSharper disable once CompareOfFloatsByEqualityOperator
            if (_configurationReader.StackTraceLimit != 0 && _configurationReader.SpanFramesMinDurationInMilliseconds != 0)
            {
                if (Duration >= _configurationReader.SpanFramesMinDurationInMilliseconds ||
                    _configurationReader.SpanFramesMinDurationInMilliseconds < 0)
                {
                    StackTrace = StacktraceHelper.GenerateApmStackTrace(new StackTrace(true).GetFrames(), _logger,
                                                                        _configurationReader, $"Span `{Name}'");
                }
            }

            _payloadSender.QueueSpan(this);

            _currentExecutionSegmentsContainer.CurrentSpan = _parentSpan;
        }
示例#7
0
        public Error(CapturedException capturedException, Transaction transaction, string parentId, IApmLogger loggerArg)
        {
            Timestamp = TimeUtils.TimestampNow();
            Id        = RandomGenerator.GenerateRandomBytesAsString(new byte[16]);

            Exception = capturedException;

            TraceId       = transaction.TraceId;
            TransactionId = transaction.Id;
            ParentId      = parentId;
            Transaction   = new TransactionData(transaction.IsSampled, transaction.Type);

            IApmLogger logger = loggerArg?.Scoped($"{nameof(Error)}.{Id}");

            logger.Trace()
            ?.Log("New Error instance created: {Error}. Time: {Time} (as timestamp: {Timestamp})",
                  this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
        }
示例#8
0
        public Span(
            string name,
            string type,
            string parentId,
            string traceId,
            Transaction enclosingTransaction,
            bool isSampled,
            IPayloadSender payloadSender,
            IApmLogger logger,
            IConfigurationReader configurationReader,
            ICurrentExecutionSegmentsContainer currentExecutionSegmentsContainer,
            Span parentSpan = null
            )
        {
            Timestamp = TimeUtils.TimestampNow();
            Id        = RandomGenerator.GenerateRandomBytesAsString(new byte[8]);
            _logger   = logger?.Scoped($"{nameof(Span)}.{Id}");

            _payloadSender       = payloadSender;
            _configurationReader = configurationReader;
            _currentExecutionSegmentsContainer = currentExecutionSegmentsContainer;
            _parentSpan           = parentSpan;
            _enclosingTransaction = enclosingTransaction;
            Name      = name;
            Type      = type;
            IsSampled = isSampled;

            ParentId = parentId;
            TraceId  = traceId;

            if (IsSampled)
            {
                // Started spans should be counted only for sampled transactions
                enclosingTransaction.SpanCount.IncrementStarted();
            }

            _currentExecutionSegmentsContainer.CurrentSpan = this;

            _logger.Trace()
            ?.Log("New Span instance created: {Span}. Start time: {Time} (as timestamp: {Timestamp}). Parent span: {Span}",
                  this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp, _parentSpan);
        }
示例#9
0
        public Span(
            string name,
            string type,
            string parentId,
            string traceId,
            Transaction enclosingTransaction,
            bool isSampled,
            IPayloadSender payloadSender,
            IApmLogger logger
            )
        {
            Timestamp = TimeUtils.TimestampNow();
            Id        = RandomGenerator.GenerateRandomBytesAsString(new byte[8]);
            _logger   = logger?.Scoped($"{nameof(Span)}.{Id}");

            _payloadSender        = payloadSender;
            _enclosingTransaction = enclosingTransaction;
            Name      = name;
            Type      = type;
            IsSampled = isSampled;

            ParentId = parentId;
            TraceId  = traceId;

            if (IsSampled)
            {
                // Started spans should be counted only for sampled transactions
                enclosingTransaction.SpanCount.Started++;

                // Spans are sent only for sampled transactions so it's only worth capturing stack trace for sampled spans
                StackTrace = StacktraceHelper.GenerateApmStackTrace(new StackTrace(true).GetFrames(), _logger, $"Span `{Name}'");
            }

            _logger.Trace()
            ?.Log("New Span instance created: {Span}. Start time: {Time} (as timestamp: {Timestamp})",
                  this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
        }
示例#10
0
        /// <summary>
        /// Creates a new transaction
        /// </summary>
        /// <param name="logger">The logger which logs debug information during the transaction  creation process</param>
        /// <param name="name">The name of the transaction</param>
        /// <param name="type">The type of the transaction</param>
        /// <param name="sampler">The sampler implementation which makes the sampling decision</param>
        /// <param name="distributedTracingData">Distributed tracing data, in case this transaction is part of a distributed trace</param>
        /// <param name="sender">The IPayloadSender implementation which will record this transaction</param>
        /// <param name="configSnapshot">The current configuration snapshot which contains the up-do-date config setting values</param>
        /// <param name="currentExecutionSegmentsContainer" />
        /// The ExecutionSegmentsContainer which makes sure this transaction flows
        /// <param name="apmServerInfo">Component to fetch info about APM Server (e.g. APM Server version)</param>
        /// <param name="ignoreActivity">
        /// If set the transaction will ignore Activity.Current and it's trace id,
        /// otherwise the agent will try to keep ids in-sync across async work-flows
        /// </param>
        internal Transaction(
            IApmLogger logger,
            string name,
            string type,
            Sampler sampler,
            DistributedTracingData distributedTracingData,
            IPayloadSender sender,
            IConfigSnapshot configSnapshot,
            ICurrentExecutionSegmentsContainer currentExecutionSegmentsContainer,
            IApmServerInfo apmServerInfo,
            bool ignoreActivity = false
            )
        {
            ConfigSnapshot = configSnapshot;
            Timestamp      = TimeUtils.TimestampNow();

            _logger        = logger?.Scoped(nameof(Transaction));
            _apmServerInfo = apmServerInfo;
            _sender        = sender;
            _currentExecutionSegmentsContainer = currentExecutionSegmentsContainer;

            Name          = name;
            HasCustomName = false;
            Type          = type;

            // For each new transaction, start an Activity if we're not ignoring them.
            // If Activity.Current is not null, the started activity will be a child activity,
            // so the traceid and tracestate of the parent will flow to it.
            if (!ignoreActivity)
            {
                _activity = StartActivity();
            }

            var isSamplingFromDistributedTracingData = false;

            if (distributedTracingData == null)
            {
                // We consider a newly created transaction **without** explicitly passed distributed tracing data
                // to be a root transaction.
                // Ignore the created activity ActivityTraceFlags because it starts out without setting the IsSampled flag,
                // so relying on that would mean a transaction is never sampled.
                if (_activity != null)
                {
                    // If an activity was created, reuse its id
                    Id      = _activity.SpanId.ToHexString();
                    TraceId = _activity.TraceId.ToHexString();

                    var idBytesFromActivity = new Span <byte>(new byte[16]);
                    _activity.TraceId.CopyTo(idBytesFromActivity);

                    // Read right most bits. From W3C TraceContext: "it is important for trace-id to carry "uniqueness" and "randomness"
                    // in the right part of the trace-id..."
                    idBytesFromActivity = idBytesFromActivity.Slice(8);

                    _traceState = new TraceState();

                    // If activity has a tracestate, populate the transaction tracestate with it.
                    if (!string.IsNullOrEmpty(_activity.TraceStateString))
                    {
                        _traceState.AddTextHeader(_activity.TraceStateString);
                    }

                    IsSampled = sampler.DecideIfToSample(idBytesFromActivity.ToArray());

                    // In the unlikely event that tracestate populated from activity contains an es vendor key, the tracestate
                    // is mutated to set the sample rate defined by the sampler, because we consider a transaction without
                    // explicitly passed distributedTracingData to be a **root** transaction. The end result
                    // is that activity tracestate will be propagated, along with the sample rate defined by this transaction.
                    if (IsSampled)
                    {
                        SampleRate = sampler.Rate;
                        _traceState.SetSampleRate(sampler.Rate);
                    }
                    else
                    {
                        SampleRate = 0;
                        _traceState.SetSampleRate(0);
                    }

                    // sync the activity tracestate with the tracestate of the transaction
                    _activity.TraceStateString = _traceState.ToTextHeader();
                }
                else
                {
                    // If no activity is created, create new random ids
                    var idBytes = new byte[8];
                    Id        = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                    IsSampled = sampler.DecideIfToSample(idBytes);

                    idBytes = new byte[16];
                    TraceId = RandomGenerator.GenerateRandomBytesAsString(idBytes);

                    if (IsSampled)
                    {
                        _traceState = new TraceState(sampler.Rate);
                        SampleRate  = sampler.Rate;
                    }
                    else
                    {
                        _traceState = new TraceState(0);
                        SampleRate  = 0;
                    }
                }

                // ParentId could be also set here, but currently in the UI each trace must start with a transaction where the ParentId is null,
                // so to avoid https://github.com/elastic/apm-agent-dotnet/issues/883 we don't set it yet.
            }
            else
            {
                if (_activity != null)
                {
                    Id = _activity.SpanId.ToHexString();

                    // try to set the parent id and tracestate on the created activity, based on passed distributed tracing data.
                    // This is so that the distributed tracing data will flow to any child activities
                    try
                    {
                        _activity.SetParentId(
                            ActivityTraceId.CreateFromString(distributedTracingData.TraceId.AsSpan()),
                            ActivitySpanId.CreateFromString(distributedTracingData.ParentId.AsSpan()),
                            distributedTracingData.FlagRecorded ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None);

                        if (distributedTracingData.HasTraceState)
                        {
                            _activity.TraceStateString = distributedTracingData.TraceState.ToTextHeader();
                        }
                    }
                    catch (Exception e)
                    {
                        _logger.Error()?.LogException(e, "Error setting trace context on created activity");
                    }
                }
                else
                {
                    var idBytes = new byte[8];
                    Id = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                }

                TraceId   = distributedTracingData.TraceId;
                ParentId  = distributedTracingData.ParentId;
                IsSampled = distributedTracingData.FlagRecorded;
                isSamplingFromDistributedTracingData = true;
                _traceState = distributedTracingData.TraceState;

                // If there is no tracestate or no valid "es" vendor entry with an "s" (sample rate) attribute, then the agent must
                // omit sample rate from non-root transactions and their spans.
                // See https://github.com/elastic/apm/blob/master/specs/agents/tracing-sampling.md#propagation
                if (_traceState?.SampleRate is null)
                {
                    SampleRate = null;
                }
                else
                {
                    SampleRate = _traceState.SampleRate.Value;
                }
            }

            // Also mark the sampling decision on the Activity
            if (IsSampled && _activity != null)
            {
                _activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
            }

            SpanCount = new SpanCount();
            _currentExecutionSegmentsContainer.CurrentTransaction = this;

            if (isSamplingFromDistributedTracingData)
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) and SampleRate ({SampleRate}) is based on incoming distributed tracing data ({DistributedTracingData})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, SampleRate, distributedTracingData, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
            else
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on the given sampler ({Sampler})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, sampler, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
        }
示例#11
0
        /// <summary>
        /// Creates a new transaction
        /// </summary>
        /// <param name="logger">The logger which logs debug information during the transaction  creation process</param>
        /// <param name="name">The name of the transaction</param>
        /// <param name="type">The type of the transaction</param>
        /// <param name="sampler">The sampler implementation which makes the sampling decision</param>
        /// <param name="distributedTracingData">Distributed tracing data, in case this transaction is part of a distributed trace</param>
        /// <param name="sender">The IPayloadSender implementation which will record this transaction</param>
        /// <param name="configSnapshot">The current configuration snapshot which contains the up-do-date config setting values</param>
        /// <param name="currentExecutionSegmentsContainer" />
        /// The ExecutionSegmentsContainer which makes sure this transaction flows
        /// <param name="ignoreActivity">
        /// If set the transaction will ignore Activity.Current and it's trace id,
        /// otherwise the agent will try to keep ids in-sync across async work-flows
        /// </param>
        internal Transaction(
            IApmLogger logger,
            string name,
            string type,
            Sampler sampler,
            DistributedTracingData distributedTracingData,
            IPayloadSender sender,
            IConfigSnapshot configSnapshot,
            ICurrentExecutionSegmentsContainer currentExecutionSegmentsContainer,
            bool ignoreActivity = false
            )
        {
            ConfigSnapshot = configSnapshot;
            Timestamp      = TimeUtils.TimestampNow();

            _logger = logger?.Scoped($"{nameof(Transaction)}");

            _sender = sender;
            _currentExecutionSegmentsContainer = currentExecutionSegmentsContainer;

            Name          = name;
            HasCustomName = false;
            Type          = type;

            // For each transaction start, we fire an Activity
            // If Activity.Current is null, then we create one with this and set its traceid, which will flow to all child activities and we also reuse it in Elastic APM, so it'll be the same on all Activities and in Elastic
            // If Activity.Current is not null, we pick up its traceid and apply it in Elastic APM
            if (!ignoreActivity)
            {
                StartActivity();
            }

            var isSamplingFromDistributedTracingData = false;

            if (distributedTracingData == null)
            {
                // Here we ignore Activity.Current.ActivityTraceFlags because it starts out without setting the IsSampled flag, so relying on that would mean a transaction is never sampled.
                // To be sure activity creation was successful let's check on it
                if (_activity != null)
                {
                    // In case activity creation was successful, let's reuse the ids
                    Id      = _activity.SpanId.ToHexString();
                    TraceId = _activity.TraceId.ToHexString();

                    var idBytesFromActivity = new Span <byte>(new byte[16]);
                    _activity.TraceId.CopyTo(idBytesFromActivity);
                    // Read right most bits. From W3C TraceContext: "it is important for trace-id to carry "uniqueness" and "randomness" in the right part of the trace-id..."
                    idBytesFromActivity = idBytesFromActivity.Slice(8);
                    IsSampled           = sampler.DecideIfToSample(idBytesFromActivity.ToArray());
                }
                else
                {
                    // In case from some reason the activity creation was not successful, let's create new random ids
                    var idBytes = new byte[8];
                    Id        = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                    IsSampled = sampler.DecideIfToSample(idBytes);

                    idBytes = new byte[16];
                    TraceId = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                }

                // PrentId could be also set here, but currently in the UI each trace must start with a transaction where the ParentId is null,
                // so to avoid https://github.com/elastic/apm-agent-dotnet/issues/883 we don't set it yet.
            }
            else
            {
                if (_activity != null)
                {
                    Id = _activity.SpanId.ToHexString();
                }
                else
                {
                    var idBytes = new byte[8];
                    Id = RandomGenerator.GenerateRandomBytesAsString(idBytes);
                }
                TraceId   = distributedTracingData.TraceId;
                ParentId  = distributedTracingData.ParentId;
                IsSampled = distributedTracingData.FlagRecorded;
                isSamplingFromDistributedTracingData = true;
                _traceState = distributedTracingData.TraceState;
            }

            // Also mark the sampling decision on the Activity
            if (IsSampled && _activity != null && !ignoreActivity)
            {
                _activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
            }

            SpanCount = new SpanCount();
            _currentExecutionSegmentsContainer.CurrentTransaction = this;

            if (isSamplingFromDistributedTracingData)
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on incoming distributed tracing data ({DistributedTracingData})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, distributedTracingData, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
            else
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on the given sampler ({Sampler})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, sampler, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }

            void StartActivity()
            {
                _activity = new Activity(ApmTransactionActivityName);
                _activity.SetIdFormat(ActivityIdFormat.W3C);
                _activity.Start();
            }
        }
示例#12
0
        public Span(
            string name,
            string type,
            string parentId,
            string traceId,
            Transaction enclosingTransaction,
            IPayloadSender payloadSender,
            IApmLogger logger,
            ICurrentExecutionSegmentsContainer currentExecutionSegmentsContainer,
            IApmServerInfo apmServerInfo,
            Span parentSpan = null,
            InstrumentationFlag instrumentationFlag = InstrumentationFlag.None,
            bool captureStackTraceOnStart           = false
            )
        {
            InstrumentationFlag = instrumentationFlag;
            Timestamp           = TimeUtils.TimestampNow();
            Id      = RandomGenerator.GenerateRandomBytesAsString(new byte[8]);
            _logger = logger?.Scoped($"{nameof(Span)}.{Id}");

            _payloadSender = payloadSender;
            _currentExecutionSegmentsContainer = currentExecutionSegmentsContainer;
            _parentSpan           = parentSpan;
            _enclosingTransaction = enclosingTransaction;
            _apmServerInfo        = apmServerInfo;
            Name = name;
            Type = type;

            ParentId = parentId;
            TraceId  = traceId;

            if (IsSampled)
            {
                SampleRate = enclosingTransaction.SampleRate;
                // Started and dropped spans should be counted only for sampled transactions
                if (enclosingTransaction.SpanCount.IncrementTotal() > ConfigSnapshot.TransactionMaxSpans &&
                    ConfigSnapshot.TransactionMaxSpans >= 0)
                {
                    _isDropped = true;
                    enclosingTransaction.SpanCount.IncrementDropped();
                }
                else
                {
                    enclosingTransaction.SpanCount.IncrementStarted();

                    // In some cases capturing the stacktrace in End() results in a stack trace which is not very useful.
                    // In such cases we capture the stacktrace on span start.
                    // These are typically async calls - e.g. capturing stacktrace for outgoing HTTP requests in the
                    // System.Net.Http.HttpRequestOut.Stop
                    // diagnostic source event produces a stack trace that does not contain the caller method in user code - therefore we
                    // capture the stacktrace is .Start
                    if (captureStackTraceOnStart && ConfigSnapshot.StackTraceLimit != 0 && ConfigSnapshot.SpanFramesMinDurationInMilliseconds != 0)
                    {
                        RawStackTrace = new StackTrace(true);
                    }
                }
            }
            else
            {
                SampleRate = 0;
            }

            _currentExecutionSegmentsContainer.CurrentSpan = this;

            _logger.Trace()
            ?.Log("New Span instance created: {Span}. Start time: {Time} (as timestamp: {Timestamp}). Parent span: {Span}",
                  this, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp, _parentSpan);
        }
示例#13
0
        internal Transaction(
            IApmLogger logger,
            string name,
            string type,
            Sampler sampler,
            DistributedTracingData distributedTracingData,
            IPayloadSender sender,
            IConfigSnapshot configSnapshot,
            ICurrentExecutionSegmentsContainer currentExecutionSegmentsContainer
            )
        {
            ConfigSnapshot = configSnapshot;
            Timestamp      = TimeUtils.TimestampNow();
            var idBytes = new byte[8];

            Id      = RandomGenerator.GenerateRandomBytesAsString(idBytes);
            _logger = logger?.Scoped($"{nameof(Transaction)}.{Id}");

            _sender = sender;
            _currentExecutionSegmentsContainer = currentExecutionSegmentsContainer;

            Name          = name;
            HasCustomName = false;
            Type          = type;

            StartActivity();

            var isSamplingFromDistributedTracingData = false;

            if (distributedTracingData == null)
            {
                // Here we ignore Activity.Current.ActivityTraceFlags because it starts out without setting the IsSampled flag, so relying on that would mean a transaction is never sampled.
                IsSampled = sampler.DecideIfToSample(idBytes);

                if (Activity.Current != null && Activity.Current.IdFormat == ActivityIdFormat.W3C)
                {
                    TraceId  = Activity.Current.TraceId.ToString();
                    ParentId = Activity.Current.ParentId;

                    // Also mark the sampling decision on the Activity
                    if (IsSampled)
                    {
                        Activity.Current.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
                    }
                }
                else
                {
                    TraceId = _activity.TraceId.ToString();
                }
            }
            else
            {
                TraceId   = distributedTracingData.TraceId;
                ParentId  = distributedTracingData.ParentId;
                IsSampled = distributedTracingData.FlagRecorded;
                isSamplingFromDistributedTracingData = true;
                _traceState = distributedTracingData.TraceState;
            }

            SpanCount = new SpanCount();

            _currentExecutionSegmentsContainer.CurrentTransaction = this;

            if (isSamplingFromDistributedTracingData)
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on incoming distributed tracing data ({DistributedTracingData})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, distributedTracingData, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }
            else
            {
                _logger.Trace()
                ?.Log("New Transaction instance created: {Transaction}. " +
                      "IsSampled ({IsSampled}) is based on the given sampler ({Sampler})." +
                      " Start time: {Time} (as timestamp: {Timestamp})",
                      this, IsSampled, sampler, TimeUtils.FormatTimestampForLog(Timestamp), Timestamp);
            }

            void StartActivity()
            {
                _activity = new Activity("ElasticApm.Transaction");
                _activity.SetIdFormat(ActivityIdFormat.W3C);
                _activity.Start();
            }
        }