private async Task ProcessQueueItems(object[] queueItems) { try { var metadataJson = _payloadItemSerializer.SerializeObject(_metadata); var ndjson = new StringBuilder(); ndjson.AppendLine("{\"metadata\": " + metadataJson + "}"); foreach (var item in queueItems) { var serialized = _payloadItemSerializer.SerializeObject(item); switch (item) { case Transaction _: ndjson.AppendLine("{\"transaction\": " + serialized + "}"); break; case Span _: ndjson.AppendLine("{\"span\": " + serialized + "}"); break; case Error _: ndjson.AppendLine("{\"error\": " + serialized + "}"); break; case MetricSet _: ndjson.AppendLine("{\"metricset\": " + serialized + "}"); break; } _logger?.Trace()?.Log("Serialized item to send: {ItemToSend} as {SerializedItem}", item, serialized); } var content = new StringContent(ndjson.ToString(), Encoding.UTF8, "application/x-ndjson"); var result = await _httpClient.PostAsync(Consts.IntakeV2Events, content, _cancellationTokenSource.Token); if (result != null && !result.IsSuccessStatusCode) { _logger?.Error() ?.Log("Failed sending event. " + "APM Server response: status code: {ApmServerResponseStatusCode}, content: \n{ApmServerResponseContent}", result.StatusCode, await result.Content.ReadAsStringAsync()); } else { _logger?.Debug() ?.Log("Sent items to server:\n{SerializedItems}", TextUtils.Indent(string.Join($",{Environment.NewLine}", queueItems.ToArray()))); } } catch (Exception e) { _logger?.Warning() ?.LogException( e, "Failed sending events. Following events were not transferred successfully to the server ({ApmServerUrl}):\n{SerializedItems}", _httpClient.BaseAddress, TextUtils.Indent(string.Join($",{Environment.NewLine}", queueItems.ToArray()))); } }
/// <summary> /// Cleans up the scheduler by indicating that no more tasks will be queued. /// This method blocks until all threads successfully shutdown. /// </summary> public void Dispose() => _disposableHelper.DoOnce(_logger, DbgName, () => { // Indicate that no new tasks will be coming in _taskQueue.CompleteAdding(); _logger.Debug()?.Log("Waiting for thread `{ThreadName}' to exit... DbgCurrentState: {DbgCurrentState}", _thread.Name , DbgCurrentState()); _thread.Join(); _logger.Debug()?.Log("Disposing _taskQueue..."); _taskQueue.Dispose(); _logger.Debug()?.Log("Done"); });
public IntakeV2EventsController(MockApmServer mockApmServer, Validator validator) { _mockApmServer = mockApmServer; _validator = validator; _logger = mockApmServer.InternalLogger.Scoped(ThisClassName + "#" + RuntimeHelpers.GetHashCode(this).ToString("X")); _logger.Debug()?.Log("Constructed with mock APM Server: {MockApmServer}", _mockApmServer); }
internal Span StartSpanInternal(string name, string type, string subType = null, string action = null) { var retVal = new Span(name, type, Id, TraceId, _enclosingTransaction, IsSampled, _payloadSender, _logger); if (!string.IsNullOrEmpty(subType)) { retVal.Subtype = subType; } if (!string.IsNullOrEmpty(action)) { retVal.Action = action; } _logger.Debug()?.Log("Starting {SpanDetails}", retVal.ToString()); return(retVal); }
public void End() { _logger.Debug()?.Log("Ending {SpanDetails}", ToString()); if (!Duration.HasValue) { Duration = (DateTimeOffset.UtcNow - _start).TotalMilliseconds; } _payloadSender.QueueSpan(this); }
public SystemTotalCpuProvider(IApmLogger logger) { _logger = logger.Scoped(nameof(SystemTotalCpuProvider)); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var categoryName = "Processor"; try { try { _processorTimePerfCounter = new PerformanceCounter(categoryName, "% Processor Time", "_Total"); } catch (InvalidOperationException e) { _logger.Debug()?.LogException(e, "Error instantiating '{CategoryName}' performance counter.", categoryName); _processorTimePerfCounter?.Dispose(); // If the Processor performance counter category does not exist, try Processor Information. categoryName = "Processor Information"; _processorTimePerfCounter = new PerformanceCounter(categoryName, "% Processor Time", "_Total"); } //The perf. counter API returns 0 the for the 1. call (probably because there is no delta in the 1. call) - so we just call it here first _processorTimePerfCounter.NextValue(); } catch (Exception e) { if (e is UnauthorizedAccessException) { _logger.Error() ?.LogException(e, "Error instantiating '{CategoryName}' performance counter." + " Process does not have permissions to read performance counters." + " See https://www.elastic.co/guide/en/apm/agent/dotnet/current/metrics.html#metrics-system to see how to configure.", categoryName); } else { _logger.Error() ?.LogException(e, "Error instantiating '{CategoryName}' performance counter", categoryName); } _logger.Warning()?.Log("System metrics won't be collected"); _processorTimePerfCounter?.Dispose(); _processorTimePerfCounter = null; } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { var(success, idle, total) = ReadProcStat(); if (!success) { return; } _prevIdleTime = idle; _prevTotalTime = total; } }
internal IisAdministration(IApmLogger logger) { _logger = logger?.Scoped(nameof(IisAdministration)); _policy = Policy .Handle <FileLoadException>() .Retry(3, (exception, retryCount) => { _logger.Debug()?.LogException(exception, "Error commiting changes. retry {RetryCount}", retryCount); }); }
internal int FindAvailablePortToListen() { var numberOfPortsTried = 0; var numberOfPortsInScanRange = PortScanRange.End - PortScanRange.Begin; var currentPort = RandomGenerator.GetInstance().Next(PortScanRange.Begin, PortScanRange.End); while (true) { ++numberOfPortsTried; try { _logger.Debug()?.Log("Trying to listen on port {PortNumber}...", currentPort); var listener = new HttpListener(); listener.Prefixes.Add($"http://localhost:{currentPort}/"); listener.Start(); listener.Stop(); _logger.Debug()?.Log("Port {PortNumber} is available - it will be used to accept connections from the agent", currentPort); return(currentPort); } catch (HttpListenerException ex) { _logger.Debug() ?.LogException(ex, "Failed to listen on port {PortNumber}. " + "Number of ports tried so far: {NumberOfPorts} out of {NumberOfPorts}", currentPort, numberOfPortsTried, numberOfPortsInScanRange); if (numberOfPortsTried == numberOfPortsInScanRange) { throw new InvalidOperationException("Could not find an available port for Mock APM Server to listen. " + $"Ports range that was tried: [{PortScanRange.Begin}, {PortScanRange.End})"); } } if (currentPort + 1 == PortScanRange.End) { currentPort = PortScanRange.Begin; } else { ++currentPort; } } }
public void QueueTransaction(ITransaction transaction) { var res = _eventQueue.Post(transaction); _logger.Debug() ?.Log(!res ? "Failed adding Transaction to the queue, {Transaction}" : "Transaction added to the queue, {Transaction}", transaction); _eventQueue.TriggerBatch(); }
internal async Task UpdateAndWaitForAgentToApply( IReadOnlyDictionary <string, string> optionsToUpdate , TimeSpan?waitUntilNextRequest = null ) { var(dbgConfig, agentAppliedEvent) = Update(optionsToUpdate, waitUntilNextRequest); _logger.Debug() ?.Log("Waiting for agent to apply updated central configuration..." + " Central config options: {CentralConfigOptions}", dbgConfig); await agentAppliedEvent.Task; }
private IActionResult GetImpl() { _logger.Debug() ?.Log("Received get-agents-config request with query string: {QueryString}." + " Current thread: {ThreadDesc}." , Request.QueryString, DbgUtils.CurrentThreadDesc); var getAgentsConfig = _mockApmServer.GetAgentsConfig; if (getAgentsConfig == null) { return(NotFound("Get-agents-config API is not enabled")); } var result = getAgentsConfig(Request, Response); _logger.Debug()?.Log("Response to get-agents-config: {Response}", result); return(result); }
private async Task ProcessQueueItems(object[] queueItems) { try { var metadata = new Metadata { Service = _service }; var metadataJson = _payloadItemSerializer.SerializeObject(metadata); var ndjson = new StringBuilder(); ndjson.Append("{\"metadata\": " + metadataJson + "}" + "\n"); foreach (var item in queueItems) { var serialized = _payloadItemSerializer.SerializeObject(item); switch (item) { case Transaction _: ndjson.AppendLine("{\"transaction\": " + serialized + "}"); break; case Span _: ndjson.AppendLine("{\"span\": " + serialized + "}"); break; case Error _: ndjson.AppendLine("{\"error\": " + serialized + "}"); break; } _logger?.Trace()?.Log("Serialized item to send: {ItemToSend} as {SerializedItemToSend}", item, serialized); } var content = new StringContent(ndjson.ToString(), Encoding.UTF8, "application/x-ndjson"); var result = await _httpClient.PostAsync(Consts.IntakeV2Events, content); if (result != null && !result.IsSuccessStatusCode) { _logger?.Error()?.Log("Failed sending event. {ApmServerResponse}", await result.Content.ReadAsStringAsync()); } else { _logger?.Debug() ?.Log($"Sent items to server: {Environment.NewLine}{{items}}", string.Join($",{Environment.NewLine}", queueItems.ToArray())); } } catch (Exception e) { _logger?.Warning() ?.LogException( e, "Failed sending events. Following events were not transferred successfully to the server:\n{items}", string.Join($",{Environment.NewLine}", queueItems.ToArray())); } }
public PayloadSenderV2(IApmLogger logger, IConfigurationReader configurationReader, Service service, Api.System system, HttpMessageHandler handler = null ) { _service = service; _system = system; _metadata = new Metadata { Service = _service, System = _system }; _logger = logger?.Scoped(nameof(PayloadSenderV2)); var serverUrlBase = configurationReader.ServerUrls.First(); var servicePoint = ServicePointManager.FindServicePoint(serverUrlBase); servicePoint.ConnectionLeaseTimeout = DnsTimeout; servicePoint.ConnectionLimit = 20; _httpClient = new HttpClient(handler ?? new HttpClientHandler()) { BaseAddress = serverUrlBase }; _httpClient.DefaultRequestHeaders.UserAgent.Add( new ProductInfoHeaderValue($"elasticapm-{Consts.AgentName}", AdaptUserAgentValue(_service.Agent.Version))); _httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("System.Net.Http", AdaptUserAgentValue(typeof(HttpClient).Assembly.GetCustomAttribute <AssemblyFileVersionAttribute>().Version))); _httpClient.DefaultRequestHeaders.UserAgent.Add( new ProductInfoHeaderValue(AdaptUserAgentValue(_service.Runtime.Name), AdaptUserAgentValue(_service.Runtime.Version))); if (configurationReader.SecretToken != null) { _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", configurationReader.SecretToken); } Task.Factory.StartNew( () => { try { #pragma warning disable 4014 DoWork(); #pragma warning restore 4014 } catch (TaskCanceledException ex) { _logger?.Debug()?.LogExceptionWithCaller(ex); } }, CancellationToken.None, TaskCreationOptions.LongRunning, _singleThreadTaskScheduler); // Replace invalid characters by underscore. All invalid characters can be found at // https://github.com/dotnet/corefx/blob/e64cac6dcacf996f98f0b3f75fb7ad0c12f588f7/src/System.Net.Http/src/System/Net/Http/HttpRuleParser.cs#L41 string AdaptUserAgentValue(string value) => Regex.Replace(value, "[ /()<>@,:;={}?\\[\\]\"\\\\]", "_"); }
/// <summary> /// Gets the ASP.NET engine version /// </summary> /// <param name="logger">The logger</param> /// <returns>the engine version, or N/A if it cannot be found</returns> public static string GetEngineVersion(IApmLogger logger) { var aspNetVersion = "N/A"; try { // We would like to report the same ASP.NET version as the one printed at the bottom of the error page // (see https://github.com/microsoft/referencesource/blob/master/System.Web/ErrorFormatter.cs#L431) // It is stored in VersionInfo.EngineVersion // (see https://github.com/microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/System.Web/Util/versioninfo.cs#L91) // which is unfortunately an internal property of an internal class in System.Web assembly so we use reflection to get it const string versionInfoTypeName = "System.Web.Util.VersionInfo"; var versionInfoType = typeof(HttpRuntime).Assembly.GetType(versionInfoTypeName); if (versionInfoType == null) { logger.Error() ?.Log("Type {TypeName} was not found in assembly {AssemblyFullName} - {AspNetVersion} will be used as ASP.NET version", versionInfoTypeName, typeof(HttpRuntime).Assembly.FullName, aspNetVersion); return(aspNetVersion); } const string engineVersionPropertyName = "EngineVersion"; var engineVersionProperty = versionInfoType.GetProperty(engineVersionPropertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (engineVersionProperty == null) { logger.Error() ?.Log("Property {PropertyName} was not found in type {TypeName} - {AspNetVersion} will be used as ASP.NET version", engineVersionPropertyName, versionInfoType.FullName, aspNetVersion); return(aspNetVersion); } var engineVersionPropertyValue = (string)engineVersionProperty.GetValue(null); if (engineVersionPropertyValue == null) { logger.Error() ?.Log("Property {PropertyName} (in type {TypeName}) is of type {TypeName} and not a string as expected" + " - {AspNetVersion} will be used as ASP.NET version", engineVersionPropertyName, versionInfoType.FullName, engineVersionPropertyName.GetType().FullName, aspNetVersion); return(aspNetVersion); } aspNetVersion = engineVersionPropertyValue; } catch (Exception ex) { logger.Error()?.LogException(ex, "Failed to obtain ASP.NET version - {AspNetVersion} will be used as ASP.NET version", aspNetVersion); } logger.Debug()?.Log("Found ASP.NET version: {AspNetVersion}", aspNetVersion); return(aspNetVersion); }
protected async Task <SampleAppResponse> SendGetRequestToSampleAppAndVerifyResponse(Uri uri, int expectedStatusCode, bool timeHttpCall = true, bool addTraceContextHeaders = false ) { var startTime = DateTime.UtcNow; if (timeHttpCall) { _logger.Debug() ?.Log("HTTP call to sample application started at {Time} (as timestamp: {Timestamp})", startTime, TimeUtils.ToTimestamp(startTime)); } try { var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri); if (addTraceContextHeaders) { httpRequestMessage.Headers.Add("traceparent", "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"); httpRequestMessage.Headers.Add("tracestate", "rojo=00f067aa0ba902b7,congo=t61rcWkgMzE"); } var response = await SendGetRequestToSampleAppAndVerifyResponseImpl(httpRequestMessage, expectedStatusCode); return(new SampleAppResponse(response.Headers, await response.Content.ReadAsStringAsync())); } finally { if (timeHttpCall) { _sampleAppClientCallTiming.Should().BeNull(); var endTime = DateTime.UtcNow; _logger.Debug() ?.Log("HTTP call to sample application ended at {Time} (as timestamp: {Timestamp}), Duration: {Duration}ms", endTime, TimeUtils.ToTimestamp(endTime), TimeUtils.DurationBetweenTimestamps(TimeUtils.ToTimestamp(startTime), TimeUtils.ToTimestamp(endTime))); _sampleAppClientCallTiming = new TimedEvent(startTime, endTime); } } }
public static IDictionary GetEnvironmentVariables(IApmLogger logger) { try { return(Environment.GetEnvironmentVariables()); } catch (Exception e) { logger.Debug()?.LogException(e, "Error while getting environment variables"); } return(null); }
internal IisAdministration(IApmLogger logger) { _logger = logger?.Scoped(nameof(IisAdministration)); _policy = Policy .Handle <FileLoadException>() .WaitAndRetry( 20, (_, _) => TimeSpan.FromMilliseconds(500), (exception, _, ctx) => { _logger.Debug()?.LogException(exception, "Error commiting changes. retry {RetryCount}", ctx.Count); }); }
public PayloadSenderV2(IApmLogger logger, IConfigSnapshot config, Service service, Api.System system, HttpMessageHandler httpMessageHandler = null, string dbgName = null ) : base(/* isEnabled: */ true, logger, ThisClassName, service, config, httpMessageHandler) { _logger = logger?.Scoped(ThisClassName + (dbgName == null ? "" : $" (dbgName: `{dbgName}')")); _payloadItemSerializer = new PayloadItemSerializer(config); _intakeV2EventsAbsoluteUrl = BackendCommUtils.ApmServerEndpoints.BuildIntakeV2EventsAbsoluteUrl(config.ServerUrls.First()); System = system; _metadata = new Metadata { Service = service, System = System }; foreach (var globalLabelKeyValue in config.GlobalLabels) { _metadata.Labels.Add(globalLabelKeyValue.Key, globalLabelKeyValue.Value); } if (config.MaxQueueEventCount < config.MaxBatchEventCount) { _logger?.Error() ?.Log( "MaxQueueEventCount is less than MaxBatchEventCount - using MaxBatchEventCount as MaxQueueEventCount." + " MaxQueueEventCount: {MaxQueueEventCount}." + " MaxBatchEventCount: {MaxBatchEventCount}.", config.MaxQueueEventCount, config.MaxBatchEventCount); _maxQueueEventCount = config.MaxBatchEventCount; } else { _maxQueueEventCount = config.MaxQueueEventCount; } _flushInterval = config.FlushInterval; _logger?.Debug() ?.Log( "Using the following configuration options:" + " Events intake API absolute URL: {EventsIntakeAbsoluteUrl}" + ", FlushInterval: {FlushInterval}" + ", MaxBatchEventCount: {MaxBatchEventCount}" + ", MaxQueueEventCount: {MaxQueueEventCount}" , _intakeV2EventsAbsoluteUrl, _flushInterval.ToHms(), config.MaxBatchEventCount, _maxQueueEventCount); _eventQueue = new BatchBlock <object>(config.MaxBatchEventCount); TransactionFilters.Add(new TransactionIgnoreUrlsFilter(config).Filter); StartWorkLoop(); }
internal static void DoSwallowingExceptions(IApmLogger logger, Action action, bool shouldSwallowCancellation = true , [CallerMemberName] string dbgCallerMethodName = null) { try { logger.Debug()?.Log(MethodStartingMsgFmt, dbgCallerMethodName); action(); logger.Debug()?.Log(MethodExitingNormallyMsgFmt, dbgCallerMethodName); } catch (OperationCanceledException ex) { logger.Debug()?.LogException(ex, MethodExitingCancelledMsgFmt, dbgCallerMethodName); if (!shouldSwallowCancellation) { throw; } } catch (Exception ex) { logger.Error()?.LogException(ex, MethodExitingExceptionMsgFmt, dbgCallerMethodName); } }
static ElasticApmModule() { var configReader = new FullFrameworkConfigReader(ConsoleLogger.Instance); var agentComponents = new AgentComponents(configurationReader: configReader); SetServiceInformation(agentComponents.Service); Agent.Setup(agentComponents); Logger = Agent.Instance.Logger.Scoped(nameof(ElasticApmModule)); Logger.Debug() ?.Log($"Entered {nameof(ElasticApmModule)} static ctor: ASP.NET: {AspNetVersion}, CLR: {ClrDescription}, IIS: {IisVersion}"); IsCaptureHeadersEnabled = Agent.Instance.ConfigurationReader.CaptureHeaders; Agent.Instance.Subscribe(new HttpDiagnosticsSubscriber()); }
private void RunOnCurrentThread() { _logger.Debug()?.Log("`{ThreadName}' thread started", Thread.CurrentThread.Name); _isExecuting = true; ExceptionUtils.DoSwallowingExceptions(_logger, () => { foreach (var task in _taskQueue.GetConsumingEnumerable(_cancellationToken)) { TryExecuteTask(task); } } , dbgCallerMethodName: $"`{Thread.CurrentThread.Name}' (ManagedThreadId: {Thread.CurrentThread.ManagedThreadId}) thread"); _isExecuting = false; }
private void OnBeginRequest(object eventSender, EventArgs eventArgs) { Logger.Debug()?.Log("Incoming request processing started - starting trace"); try { ProcessBeginRequest(eventSender); } catch (Exception ex) { Logger.Error()?.Log("Processing BeginRequest event failed. Exception: {Exception}", ex); } }
public PayloadSenderV2(IApmLogger logger, IConfigSnapshot config, Service service, Api.System system, HttpMessageHandler httpMessageHandler = null, string dbgName = null ) : base(/* isEnabled: */ true, logger, ThisClassName, service, config, httpMessageHandler) { _logger = logger?.Scoped(ThisClassName + (dbgName == null ? "" : $" (dbgName: `{dbgName}')")); System = system; _metadata = new Metadata { Service = service, System = System }; if (config.MaxQueueEventCount < config.MaxBatchEventCount) { _logger?.Error() ?.Log( "MaxQueueEventCount is less than MaxBatchEventCount - using MaxBatchEventCount as MaxQueueEventCount." + " MaxQueueEventCount: {MaxQueueEventCount}." + " MaxBatchEventCount: {MaxBatchEventCount}.", config.MaxQueueEventCount, config.MaxBatchEventCount); _maxQueueEventCount = config.MaxBatchEventCount; } else { _maxQueueEventCount = config.MaxQueueEventCount; } _flushInterval = config.FlushInterval; _logger?.Debug() ?.Log( "Using the following configuration options:" + " FlushInterval: {FlushInterval}" + ", MaxBatchEventCount: {MaxBatchEventCount}" + ", MaxQueueEventCount: {MaxQueueEventCount}" , _flushInterval.ToHms(), config.MaxBatchEventCount, _maxQueueEventCount); _eventQueue = new BatchBlock <object>(config.MaxBatchEventCount); StartWorkLoop(); }
internal bool EnqueueEvent(object eventObj, string dbgEventKind) { ThrowIfDisposed(); // Enforce _maxQueueEventCount manually instead of using BatchBlock's BoundedCapacity // because of the issue of Post returning false when TriggerBatch is in progress. For more details see // https://stackoverflow.com/questions/35626955/unexpected-behaviour-tpl-dataflow-batchblock-rejects-items-while-triggerbatch var newEventQueueCount = Interlocked.Increment(ref _eventQueueCount); if (newEventQueueCount > _maxQueueEventCount) { _logger.Debug() ?.Log("Queue reached max capacity - " + dbgEventKind + " will be discarded." + " newEventQueueCount: {EventQueueCount}." + " MaxQueueEventCount: {MaxQueueEventCount}." + " " + dbgEventKind + ": {" + dbgEventKind + "}." , newEventQueueCount, _maxQueueEventCount, eventObj); Interlocked.Decrement(ref _eventQueueCount); return(false); } var enqueuedSuccessfully = _eventQueue.Post(eventObj); if (!enqueuedSuccessfully) { _logger.Debug() ?.Log("Failed to enqueue " + dbgEventKind + "." + " newEventQueueCount: {EventQueueCount}." + " MaxQueueEventCount: {MaxQueueEventCount}." + " " + dbgEventKind + ": {" + dbgEventKind + "}." , newEventQueueCount, _maxQueueEventCount, eventObj); Interlocked.Decrement(ref _eventQueueCount); return(false); } _logger.Debug() ?.Log("Enqueued " + dbgEventKind + "." + " newEventQueueCount: {EventQueueCount}." + " MaxQueueEventCount: {MaxQueueEventCount}." + " " + dbgEventKind + ": {" + dbgEventKind + "}." , newEventQueueCount, _maxQueueEventCount, eventObj); if (_flushInterval == TimeSpan.Zero) { _eventQueue.TriggerBatch(); } return(true); }
/// <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="dbgCapturingFor">Just for logging.</param> /// <param name="configurationReader"> /// Config reader - this controls the collection of stack traces (e.g. limit on frames, /// etc) /// </param> /// <param name="apmServerInfo">The server info instance to query the APM Server version</param> /// <returns>A prepared List that can be passed to the APM server</returns> internal static List <CapturedStackFrame> GenerateApmStackTrace(Exception exception, IApmLogger logger, string dbgCapturingFor, IConfigurationReader configurationReader, IApmServerInfo apmServerInfo ) { var stackTraceLimit = configurationReader.StackTraceLimit; if (stackTraceLimit == 0) { return(null); } try { try { return(GenerateApmStackTrace( new EnhancedStackTrace(exception).GetFrames(), logger, configurationReader, apmServerInfo, dbgCapturingFor)); } catch (Exception e) { logger?.Debug() ? .LogException(e, "Failed generating stack trace with EnhancedStackTrace - using fallback without demystification"); // Fallback, see https://github.com/elastic/apm-agent-dotnet/issues/957 // This callstack won't be demystified, but at least it'll be captured. return(GenerateApmStackTrace( new StackTrace(exception, true).GetFrames(), logger, configurationReader, apmServerInfo, dbgCapturingFor)); } } catch (Exception e) { logger?.Warning() ?.Log("Failed extracting stack trace from exception for {ApmContext}." + " Exception for failure to extract: {ExceptionForFailureToExtract}." + " Exception to extract from: {ExceptionToExtractFrom}.", dbgCapturingFor, e, exception); } return(null); }
public PayloadSenderV2(IApmLogger logger, IConfigurationReader configurationReader, Service service, Api.System system, HttpMessageHandler handler = null) { _service = service; _system = system; _metadata = new Metadata { Service = _service, System = _system }; _logger = logger?.Scoped(nameof(PayloadSenderV2)); var serverUrlBase = configurationReader.ServerUrls.First(); var servicePoint = ServicePointManager.FindServicePoint(serverUrlBase); servicePoint.ConnectionLeaseTimeout = DnsTimeout; servicePoint.ConnectionLimit = 20; _httpClient = new HttpClient(handler ?? new HttpClientHandler()) { BaseAddress = serverUrlBase }; if (configurationReader.SecretToken != null) { _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", configurationReader.SecretToken); } Task.Factory.StartNew( () => { try { #pragma warning disable 4014 DoWork(); #pragma warning restore 4014 } catch (TaskCanceledException ex) { _logger?.Debug()?.LogExceptionWithCaller(ex); } }, CancellationToken.None, TaskCreationOptions.LongRunning, _singleThreadTaskScheduler); }
/// <summary> /// We need this private ctor to avoid calling configStore.CurrentSnapshot twice (and thus possibly using different /// snapshots) /// when passing isEnabled: initialConfigSnapshot.CentralConfig and config: initialConfigSnapshot to base /// </summary> private CentralConfigFetcher(IApmLogger logger, IConfigStore configStore, IConfigSnapshot initialConfigSnapshot, Service service , HttpMessageHandler httpMessageHandler, IAgentTimer agentTimer, string dbgName ) : base(/* isEnabled: */ initialConfigSnapshot.CentralConfig, logger, ThisClassName, service, initialConfigSnapshot, httpMessageHandler) { _logger = logger?.Scoped(ThisClassName + (dbgName == null ? "" : $" (dbgName: `{dbgName}')")); _initialSnapshot = initialConfigSnapshot; var isCentralConfigOptEqDefault = _initialSnapshot.CentralConfig == ConfigConsts.DefaultValues.CentralConfig; var centralConfigStatus = _initialSnapshot.CentralConfig ? "enabled" : "disabled"; if (!isCentralConfigOptEqDefault) { centralConfigStatus = centralConfigStatus.ToUpper(); } _logger.IfLevel(isCentralConfigOptEqDefault ? LogLevel.Debug : LogLevel.Information) ?.Log("Central configuration feature is {CentralConfigStatus} because CentralConfig option's value is {CentralConfigOptionValue}" + " (default value is {CentralConfigOptionDefaultValue})" , centralConfigStatus, _initialSnapshot.CentralConfig, ConfigConsts.DefaultValues.CentralConfig); if (!_initialSnapshot.CentralConfig) { return; } _configStore = configStore; _agentTimer = agentTimer ?? new AgentTimer(); _getConfigAbsoluteUrl = BackendCommUtils.ApmServerEndpoints.BuildGetConfigAbsoluteUrl(initialConfigSnapshot.ServerUrls.First(), service); _logger.Debug() ?.Log("Combined absolute URL for APM Server get central configuration endpoint: `{Url}'. Service: {Service}." , _getConfigAbsoluteUrl, service); StartWorkLoop(); }
internal BackendCommComponentBase(bool isEnabled, IApmLogger logger, string dbgDerivedClassName, Service service , IConfiguration configuration, HttpMessageHandler httpMessageHandler = null ) { _dbgName = $"{ThisClassName} ({dbgDerivedClassName})"; _dbgDerivedClassName = dbgDerivedClassName; _logger = logger?.Scoped(_dbgName); _isEnabled = isEnabled; if (!_isEnabled) { _logger.Debug()?.Log("Disabled - exiting without initializing any members used by work loop"); return; } CancellationTokenSource = new CancellationTokenSource(); _disposableHelper = new DisposableHelper(); _loopStarted = new ManualResetEventSlim(); _loopCompleted = new ManualResetEventSlim(); HttpClient = BackendCommUtils.BuildHttpClient(logger, configuration, service, _dbgName, httpMessageHandler); }
public async Task InvokeAsync(HttpContext context) { Transaction transaction; var transactionName = $"{context.Request.Method} {context.Request.Path}"; if (context.Request.Headers.ContainsKey(TraceParent.TraceParentHeaderName)) { var headerValue = context.Request.Headers[TraceParent.TraceParentHeaderName].ToString(); var distributedTracingData = TraceParent.TryExtractTraceparent(headerValue); if (distributedTracingData != null) { _logger.Debug() ?.Log( "Incoming request with {TraceParentHeaderName} header. DistributedTracingData: {DistributedTracingData}. Continuing trace.", TraceParent.TraceParentHeaderName, distributedTracingData); transaction = _tracer.StartTransactionInternal( transactionName, ApiConstants.TypeRequest, distributedTracingData); } else { _logger.Debug() ?.Log( "Incoming request with invalid {TraceParentHeaderName} header (received value: {TraceParentHeaderValue}). Starting trace with new trace id.", TraceParent.TraceParentHeaderName, headerValue); transaction = _tracer.StartTransactionInternal(transactionName, ApiConstants.TypeRequest); } } else { _logger.Debug()?.Log("Incoming request. Starting Trace."); transaction = _tracer.StartTransactionInternal(transactionName, ApiConstants.TypeRequest); } if (transaction.IsSampled) { FillSampledTransactionContextRequest(context, transaction); } try { await _next(context); } catch (Exception e) when((Helpers.ExceptionFilter.Capture(e, transaction, context, _configurationReader, _logger))) { } finally { if (!transaction.HasCustomName) { //fixup Transaction.Name - e.g. /user/profile/1 -> /user/profile/{id} var routeData = (context.Features[typeof(IRoutingFeature)] as IRoutingFeature)?.RouteData; if (routeData != null) { var name = GetNameFromRouteContext(routeData.Values); if (!string.IsNullOrWhiteSpace(name)) { transaction.Name = $"{context.Request.Method} {name}"; } } } transaction.Result = Transaction.StatusCodeToResult(GetProtocolName(context.Request.Protocol), context.Response.StatusCode); if (transaction.IsSampled) { FillSampledTransactionContextResponse(context, transaction); FillSampledTransactionContextUser(context, transaction); } transaction.End(); } }
private void PostToInternalTaskScheduler(string dbgActionDesc, Func <Task> asyncAction , TaskCreationOptions taskCreationOptions = TaskCreationOptions.None ) { #pragma warning disable 4014 // We don't pass any CancellationToken on purpose because in some case (for example work loop) // we wait for asyncAction to start so we should never cancel it before it starts Task.Factory.StartNew(asyncAction, CancellationToken.None, taskCreationOptions, _singleThreadTaskScheduler); #pragma warning restore 4014 _logger.Debug()?.Log("Posted {DbgTaskDesc} to internal task scheduler", dbgActionDesc); }