internal static void AssertValid(this CapturedStackFrame thisObj) { thisObj.Should().NotBeNull(); thisObj.FileName.Should().NotBeNullOrEmpty(); thisObj.LineNo.Should().BeGreaterOrEqualTo(0); thisObj.Module?.Should().NotBeEmpty(); thisObj.Function?.Should().NotBeEmpty(); }
private static void FullFwAssertValid(CapturedStackFrame capturedStackFrame) { capturedStackFrame.Should().NotBeNull(); capturedStackFrame.Function.NonEmptyAssertValid(); }
/// <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="apmServerInfo">The ServerInfo instance to query the server version</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> /// <returns>A prepared List that can be passed to the APM server</returns> internal static List <CapturedStackFrame> GenerateApmStackTrace(StackFrame[] frames, IApmLogger logger, IConfigurationReader configurationReader, IApmServerInfo apmServerInfo, string dbgCapturingFor ) { var stackTraceLimit = configurationReader.StackTraceLimit; if (stackTraceLimit == 0) { return(null); } if (stackTraceLimit > 0) { // new StackTrace(skipFrames: n) skips frames from the top of the stack (currently executing method is top) // the StackTraceLimit feature takes the top n frames, so unfortunately we currently capture the whole stack trace and just take // the top `configurationReader.StackTraceLimit` frames. - This could be optimized. frames = frames.Take(stackTraceLimit).ToArray(); } var retVal = new List <CapturedStackFrame>(frames.Length); logger.Trace()?.Log("transform stack frames"); try { foreach (var frame in frames) { var className = frame?.GetMethod() ?.DeclaringType?.FullName; //see: https://github.com/elastic/apm-agent-dotnet/pull/240#discussion_r289619196 var functionName = GetRealMethodName(frame?.GetMethod()); var fileName = frame?.GetFileName(); logger.Trace()?.Log("{MethodName}, {lineNo}", functionName, frame?.GetFileLineNumber()); var capturedStackFrame = new CapturedStackFrame { Function = functionName ?? "N/A", Module = frame?.GetMethod()?.ReflectedType?.Assembly.FullName, LineNo = frame?.GetFileLineNumber() ?? 0, AbsPath = frame?.GetFileName() // optional property }; if (apmServerInfo?.Version < V710) { // In pre 7.10, Kibana shows stack traces in format: `[FileName] in [MethodName]` and there is no way to show ClassName. // For .NET that format is less useful especially because in some cases we only have a `.dll` file as filename. // Therefore as a workaround we send the real classname in the file name field and // we don't send anything in the ClassName field, since that's not used. // If versions 7.09 is out of support, this code can be removed. capturedStackFrame.FileName = string.IsNullOrWhiteSpace(className) ? "N/A" : className; } else { // FileName is either the .cs file or the assembly location as fallback capturedStackFrame.FileName = string.IsNullOrWhiteSpace(fileName) ? string.IsNullOrEmpty(frame?.GetMethod()?.GetType().Assembly.Location) ? "n/a" : frame.GetMethod()?.GetType().Assembly.Location : fileName; capturedStackFrame.ClassName = string.IsNullOrWhiteSpace(className) ? "N/A" : className; } retVal.Add(capturedStackFrame); } } catch (Exception e) { logger?.Warning()?.LogException(e, "Failed capturing stacktrace for {ApmContext}", dbgCapturingFor); } return(retVal); }