예제 #1
0
 internal DbConnectionStringParser(IApmLogger logger) => _logger = logger.Scoped(ThisClassName);
예제 #2
0
 /// <summary>
 /// If the logger has a loglevel, which is higher than Info then it returns a MaybeLogger instance,
 /// otherwise it returns null.
 /// By using the return value with `?.` you can avoid executing code that is not necessary to execute
 /// in case the log won't be printed because the loglevel would not allow it.
 /// </summary>
 /// <param name="logger">The logger instance you want to log with</param>
 /// <returns>Either a MaybeLogger or null</returns>
 internal static MaybeLogger?Info(this IApmLogger logger) => IfLevel(logger, LogLevel.Information);
예제 #3
0
 internal static ScopedLogger Scoped(this IApmLogger logger, string scope) =>
 public AzureCloudMetadataProvider(IApmLogger logger) : this(logger, new HttpClientHandler())
 {
 }
예제 #5
0
 internal ConfigStateC(IApmLogger logger)
 {
     _logger         = logger.Scoped(ThisClassName);
     _appliedByAgent = new TaskCompletionSource <object>();
 }
예제 #6
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);
        }
 public FullFrameworkConfigReader(IApmLogger logger = null)
     : base(logger, /* defaultEnvironmentName: */ null, ThisClassName) => _logger = logger?.Scoped(ThisClassName);
 public CloudMetadataProviderCollection(string cloudProvider, IApmLogger logger)
     : this(cloudProvider, logger, new EnvironmentVariables(logger))
 {
 }
예제 #9
0
 internal MockConfigSnapshot BuildConfig(IApmLogger logger) =>
 new MockConfigSnapshot(logger
                        , flushInterval: FlushInterval.HasValue ? $"{FlushInterval.Value.TotalMilliseconds}ms" : null
                        , maxBatchEventCount: MaxBatchEventCount?.ToString()
                        , maxQueueEventCount: MaxQueueEventCount?.ToString());
예제 #10
0
        /// <summary>
        ///  Turns an <see cref="Exception"/> into a <see cref="CapturedStackFrame" /> list which can be reported to the APM Server
        /// </summary>
        /// <param name="exception">The exception to rewrite into APM stack traces</param>
        /// <param name="logger">The logger to emit exceptions on should one occur</param>
        /// <param name="capturingFor">Just for logging.</param>
        /// <returns>A prepared List that can be passed to the APM server</returns>
        internal static List <CapturedStackFrame> GenerateApmStackTrace(Exception exception, IApmLogger logger, string capturingFor)
        {
            try
            {
                return(GenerateApmStackTrace(new StackTrace(exception, true).GetFrames(), logger, capturingFor));
            }
            catch (Exception e)
            {
                logger?.Warning()?.LogException(e, "Failed extracting exception from stackTrace for {ApmContext}", capturingFor);
            }

            return(null);
        }
 public EnvironmentVariables(IApmLogger logger) => _logger = logger.Scoped(nameof(EnvironmentVariables));
예제 #12
0
        /// <summary>
        /// Turns a System.Diagnostic.StackFrame[] into a <see cref="CapturedStackFrame" /> list which can be reported to the APM Server
        /// </summary>
        /// <param name="frames">The stack frames to rewrite into APM stack traces</param>
        /// <param name="logger">The logger to emit exceptions on should one occur</param>
        /// <param name="capturingFor">Just for logging.</param>
        /// <returns>A prepared List that can be passed to the APM server</returns>
        internal static List <CapturedStackFrame> GenerateApmStackTrace(StackFrame[] frames, IApmLogger logger, string capturingFor)
        {
            var retVal = new List <CapturedStackFrame>(frames.Length);

            try
            {
                retVal.AddRange(from item in frames
                                let fileName = item?.GetMethod()?.DeclaringType?.Assembly?.GetName()?.Name
                                               where !string.IsNullOrEmpty(fileName)
                                               select new CapturedStackFrame
                {
                    Function = item?.GetMethod()?.Name,
                    FileName = fileName,
                    Module   = item?.GetMethod()?.ReflectedType?.Name,
                    LineNo   = item?.GetFileLineNumber() ?? 0
                });
            }
            catch (Exception e)
            {
                logger?.Warning()?.LogException(e, "Failed capturing stacktrace for {ApmContext}", capturingFor);
            }

            return(retVal);
        }
예제 #13
0
 internal DbSpanCommon(IApmLogger logger) => _dbConnectionStringParser = new DbConnectionStringParser(logger);
예제 #14
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);
            }
        }
 internal CentralConfigFetcher(IApmLogger logger, IConfigStore configStore, ICentralConfigResponseParser centralConfigResponseParser,
                               Service service,
                               HttpMessageHandler httpMessageHandler = null, IAgentTimer agentTimer = null, string dbgName = null
                               ) : this(logger, configStore, configStore.CurrentSnapshot, service, httpMessageHandler, agentTimer, dbgName) =>
예제 #16
0
        public PayloadSenderTests(ITestOutputHelper xUnitOutputHelper) : base(xUnitOutputHelper /*, LogLevel.Debug */)
        {
            _logger = LoggerBase.Scoped(ThisClassName);
//			LoggerBase.Level = LogLevel.Debug;
        }
예제 #17
0
 public TestSystemInfoHelper(IApmLogger logger, string lineInCroup) : base(logger)
     => _lineInCroup = lineInCroup;
예제 #18
0
        /// <summary>
        /// Extracts the request body using measure to prevent the 'read once' problem (cannot read after the body ha been already
        /// read).
        /// </summary>
        /// <param name="request"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static async Task <string> ExtractRequestBodyAsync(this HttpRequest request, IApmLogger logger, IConfigSnapshot configSnapshot)
        {
            string body = null;

            try
            {
                if (request.HasFormContentType)
                {
                    var form = await request.ReadFormAsync();

                    var itemProcessed = 0;
                    if (form != null && form.Count > 0)
                    {
                        var sb = new StringBuilder();

                        foreach (var item in form)
                        {
                            sb.Append(item.Key);
                            sb.Append("=");

                            if (WildcardMatcher.IsAnyMatch(configSnapshot.SanitizeFieldNames, item.Key))
                            {
                                sb.Append(Elastic.Apm.Consts.Redacted);
                            }
                            else
                            {
                                sb.Append(item.Value);
                            }

                            itemProcessed++;
                            if (itemProcessed != form.Count)
                            {
                                sb.Append("&");
                            }
                        }

                        body = sb.ToString();
                    }
                }
                else
                {
                    request.EnableBuffering();
                    request.Body.Position = 0;

                    using (var reader = new StreamReader(request.Body,
                                                         Encoding.UTF8,
                                                         false,
                                                         1024 * 2,
                                                         true))
                        body = await reader.ReadToEndAsync();

                    // Truncate the body to the first 2kb if it's longer
                    if (body.Length > Consts.RequestBodyMaxLength)
                    {
                        body = body.Substring(0, Consts.RequestBodyMaxLength);
                    }
                    request.Body.Position = 0;
                }
            }
            catch (IOException ioException)
            {
                logger.Error()?.LogException(ioException, "IO Error reading request body");
            }
            catch (Exception e)
            {
                logger.Error()?.LogException(e, "Error reading request body");
            }
            return(body);
        }
 protected AbstractConfigurationReader(IApmLogger logger) => ScopedLogger = logger?.Scoped(GetType().Name);
 internal SystemTotalCpuProvider(IApmLogger logger, StreamReader procStatStreamReader)
 => (_logger, _procStatStreamReader) = (logger.Scoped(nameof(SystemTotalCpuProvider)), procStatStreamReader);
 internal AzureCloudMetadataProvider(IApmLogger logger, HttpMessageHandler handler)
 {
     _handler = handler;
     _logger  = logger.Scoped(nameof(AzureCloudMetadataProvider));
 }
예제 #22
0
 public ScopedLogger(IApmLogger logger, string scope) => (Logger, Scope) = (logger, scope);
예제 #23
0
        public Span(string name, string type, Transaction transaction, IPayloadSender payloadSender, IApmLogger logger)
        {
            _start         = DateTimeOffset.UtcNow;
            _payloadSender = payloadSender;
            _logger        = logger?.Scoped(nameof(Span));
            Name           = name;
            Type           = type;

            Id            = RandomGenerator.GetRandomBytesAsString(new byte[8]);
            ParentId      = transaction.Id;
            TransactionId = transaction.Id;
            TraceId       = transaction.TraceId;
        }
예제 #24
0
 protected AbstractConfigurationReader(IApmLogger logger, string dbgDerivedClassName) =>
예제 #25
0
 public SanitizeFieldNamesTests(WebApplicationFactory <Startup> factory)
 {
     _logger  = new TestLogger();
     _factory = factory;
 }
예제 #26
0
 public SystemInfoHelper(IApmLogger logger)
 => _logger = logger.Scoped(nameof(SystemInfoHelper));
예제 #27
0
 public MaybeLogger(IApmLogger logger, LogLevel level) => (_logger, _level) = (logger, level);
예제 #28
0
 public MetricsTests(ITestOutputHelper xUnitOutputHelper) : base(xUnitOutputHelper)
 {
     _output = xUnitOutputHelper;
     _logger = LoggerBase.Scoped(ThisClassName);
 }
예제 #29
0
 /// <summary>
 /// Depending on the two parameters it either returns a MaybeLogger instance or null.
 /// </summary>
 /// <param name="logger">The logger you want to log with</param>
 /// <param name="level">The level to compare with</param>
 /// <returns>If the return value is not null you can call <see cref="MaybeLogger.Log" /> to log</returns>
 private static MaybeLogger?IfLevel(this IApmLogger logger, LogLevel level) =>
 logger.IsEnabled(level) ? new MaybeLogger(logger, level) : (MaybeLogger?)null;
예제 #30
0
        internal static Service GetDefaultService(IConfigurationReader configurationReader, IApmLogger loggerArg)
        {
            IApmLogger logger = loggerArg.Scoped(nameof(Service));

            return(new Service
            {
                Name = configurationReader.ServiceName,
                Agent = new AgentC
                {
                    Name = Consts.AgentName,
                    Version = typeof(Agent).Assembly.GetCustomAttribute <AssemblyInformationalVersionAttribute>().InformationalVersion
                },
                Runtime = PlatformDetection.GetServiceRuntime(logger)
            });
        }