//----------------------------------------------------------------------------- // // Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. // A copy of the License is located at // // http://aws.amazon.com/apache2.0 // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. // //----------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Amazon.Runtime; using Amazon.Runtime.Internal.Util; using Amazon.XRay.Recorder.Core.Internal.Entities; namespace Amazon.XRay.Recorder.Core.Strategies { /// /// Defines default startegy for recording exception. By default class exeptions are marked as remote. /// [Serializable] public class DefaultExceptionSerializationStrategy : ExceptionSerializationStrategy { private static readonly Logger _logger = Logger.GetLogger(typeof(DefaultExceptionSerializationStrategy)); private static List _defaultExceptionClasses = new List() { typeof(AmazonServiceException)}; private List _remoteExceptionClasses = new List(); /// /// Default stack frame size for the recorded . /// public const int DefaultStackFrameSize = 50; /// /// The maximum stack frame size for the strategy. /// public int MaxStackFrameSize { get; private set; } = DefaultStackFrameSize; /// /// Initializes a new instance of the class. /// public DefaultExceptionSerializationStrategy() : this(DefaultStackFrameSize) { } /// /// Initializes instance with provided Stack frame size. /// While setting number consider max trace size limit : https://aws.amazon.com/xray/pricing/ /// /// Integer value for stack frame size. public DefaultExceptionSerializationStrategy(int stackFrameSize) { MaxStackFrameSize = GetValidStackFrameSize(stackFrameSize); _remoteExceptionClasses.AddRange(_defaultExceptionClasses); } /// /// Initializes instance with provided Stack frame size and /// list of types for which exceptions should be marked as remote. /// While setting number consider max trace size limit : https://aws.amazon.com/xray/pricing/ /// /// Stack frame size for the recorded exception. /// List of for which exceptions should be marked as remote. public DefaultExceptionSerializationStrategy(int stackFrameSize, List types) { MaxStackFrameSize = GetValidStackFrameSize(stackFrameSize); _remoteExceptionClasses.AddRange(types); _remoteExceptionClasses.AddRange(_defaultExceptionClasses); } /// /// Initializes instance with provided /// list of types for which exceptions should be marked as remote. /// /// List of for which exceptions should be marked as remote. public DefaultExceptionSerializationStrategy(List types) { MaxStackFrameSize = DefaultStackFrameSize; _remoteExceptionClasses.AddRange(types); _remoteExceptionClasses.AddRange(_defaultExceptionClasses); } /// /// Validates and returns valid max stack frame size. /// public static int GetValidStackFrameSize(int stackFrameSize) { if (stackFrameSize < 0) { _logger.DebugFormat("Provided Stack frame size should be non-negative. Setting max stack frame size : {0}", DefaultStackFrameSize); return DefaultStackFrameSize; } _logger.DebugFormat("Setting max stack frame size : {0}", stackFrameSize); return stackFrameSize; } /// /// Checks whether the exception should be marked as remote. /// /// Instance of . /// True if the exception is of type present in , else false. private bool IsRemoteException(Exception e) { foreach (Type t in _remoteExceptionClasses) { Type exceptionType = e.GetType(); if (exceptionType == t || exceptionType.IsSubclassOf(t)) { return true; } } return false; } /// /// Visit each node in the cause chain. For each node: /// Determine if it has already been described in one of the child subsegments' causes. If so, link there. /// Otherwise, describe it and add it to the Cause and returns the list of . /// /// The exception to be added /// The subsegments to search for existing exception descriptor. /// List of public List DescribeException(Exception e, IEnumerable subsegments) { List result = new List(); // First check if the exception has been described in subsegment ExceptionDescriptor ex = new ExceptionDescriptor(); IEnumerable existingExceptionDescriptors = null; if (subsegments != null) { existingExceptionDescriptors = subsegments.Where(subsegment => subsegment.Cause != null && subsegment.Cause.IsExceptionAdded).SelectMany(subsegment => subsegment.Cause.ExceptionDescriptors); } ExceptionDescriptor existingDescriptor = null; if (existingExceptionDescriptors != null) { existingDescriptor = existingExceptionDescriptors.FirstOrDefault(descriptor => e.Equals(descriptor.Exception)); } // While referencing exception from child, record id if exists or cause and return. if (existingDescriptor != null) { ex.Cause = existingDescriptor.Id != null ? existingDescriptor.Id : existingDescriptor.Cause; ex.Exception = existingDescriptor.Exception; // pass the exception of the cause so that this reference can be found if the same exception is thrown again ex.Id = null; // setting this to null since, cause is already populated with reference to downstream exception result.Add(ex); return result; } // The exception is not described. Start describe it. ExceptionDescriptor curDescriptor = new ExceptionDescriptor(); while (e != null) { curDescriptor.Exception = e; curDescriptor.Message = e.Message; curDescriptor.Type = e.GetType().Name; StackFrame[] frames = new StackTrace(e, true).GetFrames(); if (frames != null && frames.Length > MaxStackFrameSize) { curDescriptor.Truncated = frames.Length - MaxStackFrameSize; curDescriptor.Stack = new StackFrame[MaxStackFrameSize]; Array.Copy(frames, curDescriptor.Stack, MaxStackFrameSize); } else { curDescriptor.Stack = frames; } if (IsRemoteException(e)) { curDescriptor.Remote = true; } result.Add(curDescriptor); e = e.InnerException; if (e != null) { // Inner exception alreay described ExceptionDescriptor innerExceptionDescriptor = existingExceptionDescriptors != null ? existingExceptionDescriptors.FirstOrDefault(d => d.Exception.Equals(e)) : null; if (innerExceptionDescriptor != null) { curDescriptor.Cause = innerExceptionDescriptor.Id; e = null; } else { var newDescriptor = new ExceptionDescriptor(); curDescriptor.Cause = newDescriptor.Id; curDescriptor = newDescriptor; } } } return result; } } }