Skip to content

Commit d28feb4

Browse files
committed
remove reflection
1 parent 5661621 commit d28feb4

File tree

1 file changed

+47
-122
lines changed

1 file changed

+47
-122
lines changed

libraries/src/AWS.Lambda.Powertools.Tracing/Internal/XRayRecorder.cs

Lines changed: 47 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Diagnostics.CodeAnalysis;
23
using Amazon.XRay.Recorder.Core;
34
using Amazon.XRay.Recorder.Core.Internal.Emitters;
45
using Amazon.XRay.Recorder.Core.Internal.Entities;
@@ -172,7 +173,7 @@ private static bool IsSerializationError(Exception e)
172173

173174
var message = e.Message ?? string.Empty;
174175
var stackTrace = e.StackTrace ?? string.Empty;
175-
var typeName = e.GetType().Name ?? string.Empty;
176+
var typeName = e.GetType().Name;
176177

177178
return message.Contains("LitJson") ||
178179
message.Contains("JsonMapper") ||
@@ -461,20 +462,18 @@ private static object SanitizeCollectionTypes(object value, Type type, int depth
461462
}
462463

463464
/// <summary>
464-
/// Sanitizes array values.
465+
/// Sanitizes array values
465466
/// </summary>
466467
/// <param name="array">The array to sanitize</param>
467468
/// <param name="type">The array type</param>
468469
/// <param name="depth">Current recursion depth</param>
469470
/// <returns>Sanitized array</returns>
470471
private static object SanitizeArray(Array array, Type type, int depth)
471472
{
472-
var elementType = type.GetElementType();
473-
474-
// If it's an array of safe types and all elements are actually safe, return original array
475-
if (elementType != null && IsSafeType(elementType) && IsArrayElementsSafe(array))
473+
// Check if it's a known safe array type
474+
if (IsKnownSafeArrayType(type))
476475
{
477-
return array; // Return original array
476+
return array; // Return original array for known safe types
478477
}
479478

480479
// Otherwise, sanitize to object array
@@ -487,6 +486,20 @@ private static object SanitizeArray(Array array, Type type, int depth)
487486
return sanitizedArray;
488487
}
489488

489+
/// <summary>
490+
/// Checks if an array type is known to be safe without reflection
491+
/// </summary>
492+
private static bool IsKnownSafeArrayType(Type type)
493+
{
494+
return type == typeof(string[]) ||
495+
type == typeof(int[]) ||
496+
type == typeof(long[]) ||
497+
type == typeof(double[]) ||
498+
type == typeof(float[]) ||
499+
type == typeof(bool[]) ||
500+
type == typeof(decimal[]);
501+
}
502+
490503
/// <summary>
491504
/// Checks if all elements in an array are safe types.
492505
/// </summary>
@@ -540,63 +553,28 @@ private static object SanitizeEnumerable(System.Collections.IEnumerable enumerab
540553
}
541554

542555
/// <summary>
543-
/// Sanitizes complex objects by converting them to dictionaries.
556+
/// Sanitizes complex objects by converting them to safe string representation.
544557
/// </summary>
545558
/// <param name="value">The object to sanitize</param>
546559
/// <param name="type">The object type</param>
547560
/// <param name="depth">Current recursion depth</param>
548-
/// <returns>Sanitized dictionary representation</returns>
561+
/// <returns>Sanitized string representation</returns>
549562
private static object SanitizeComplexObject(object value, Type type, int depth)
550563
{
551564
try
552565
{
553-
var properties =
554-
type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
555-
var sanitizedObject = new System.Collections.Generic.Dictionary<string, object>();
556-
557-
foreach (var prop in properties)
558-
{
559-
var propertyValue = GetPropertyValueSafely(prop, value, depth);
560-
if (propertyValue != null)
561-
{
562-
sanitizedObject[prop.Name] = propertyValue;
563-
}
564-
}
565-
566-
return sanitizedObject;
566+
// For Native AOT compatibility, we avoid reflection and convert to string
567+
// This ensures the object can be serialized without issues
568+
return $"[{type.Name}] {value.ToString()}";
567569
}
568570
catch (Exception ex)
569571
{
570-
// If all else fails, convert to string
571-
return $"[Object conversion failed: {ex.Message}] {value.ToString()}";
572+
// If all else fails, return a safe fallback
573+
return $"[Object conversion failed: {ex.Message}]";
572574
}
573575
}
574576

575-
/// <summary>
576-
/// Safely gets a property value from an object.
577-
/// </summary>
578-
/// <param name="prop">The property to read</param>
579-
/// <param name="value">The object to read from</param>
580-
/// <param name="depth">Current recursion depth</param>
581-
/// <returns>The property value or null if it can't be read</returns>
582-
private static object GetPropertyValueSafely(System.Reflection.PropertyInfo prop, object value, int depth)
583-
{
584-
try
585-
{
586-
if (prop.CanRead && prop.GetIndexParameters().Length == 0) // Skip indexers
587-
{
588-
var propValue = prop.GetValue(value);
589-
return SanitizeValueRecursive(propValue, depth + 1);
590-
}
591-
}
592-
catch (Exception ex)
593-
{
594-
// If we can't read a property, record the error
595-
return $"[Error reading property: {ex.Message}]";
596-
}
597577

598-
return null;
599-
}
600578

601579
/// <summary>
602580
/// Determines if a type is safe for X-Ray without sanitization
@@ -632,38 +610,40 @@ private static bool NeedsTypeSanitization(Type type)
632610
type == typeof(Guid) || type.IsEnum)
633611
return true;
634612

635-
// Check for nullable versions of problematic types
636-
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
637-
{
638-
var underlyingType = Nullable.GetUnderlyingType(type);
639-
return underlyingType != null && NeedsTypeSanitization(underlyingType);
640-
}
613+
// Check for specific nullable types without reflection
614+
if (IsKnownNullableProblematicType(type))
615+
return true;
641616

642617
return false;
643618
}
644619

620+
/// <summary>
621+
/// Checks for known nullable problematic types without using reflection
622+
/// </summary>
623+
private static bool IsKnownNullableProblematicType(Type type)
624+
{
625+
return type == typeof(IntPtr?) || type == typeof(UIntPtr?) ||
626+
type == typeof(uint?) || type == typeof(ulong?) ||
627+
type == typeof(ushort?) || type == typeof(byte?) || type == typeof(sbyte?) ||
628+
type == typeof(DateTime?) || type == typeof(TimeSpan?) ||
629+
type == typeof(Guid?);
630+
}
631+
645632
/// <summary>
646633
/// Safely sanitizes the current entity to prevent JSON serialization errors.
647-
/// This method uses reflection to access and sanitize all data in the entity.
648634
/// </summary>
635+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Entity properties are preserved by X-Ray SDK")]
649636
private void SanitizeCurrentEntitySafely()
650637
{
651638
try
652639
{
653640
var entity = _awsxRayRecorder?.TraceContext?.GetEntity();
654641
if (entity == null) return;
655642

656-
// Sanitize Metadata
643+
// Sanitize known entity properties without reflection
657644
SanitizeEntityMetadata(entity);
658-
659-
// Sanitize Annotations
660645
SanitizeEntityAnnotations(entity);
661-
662-
// Sanitize HTTP information
663646
SanitizeEntityHttpInformation(entity);
664-
665-
// Sanitize any other properties that might contain problematic data
666-
SanitizeEntityOtherProperties(entity);
667647
}
668648
catch (Exception ex)
669649
{
@@ -675,6 +655,7 @@ private void SanitizeCurrentEntitySafely()
675655
/// <summary>
676656
/// Sanitizes the metadata in an entity
677657
/// </summary>
658+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Entity.Metadata property is preserved by X-Ray SDK")]
678659
private static void SanitizeEntityMetadata(Entity entity)
679660
{
680661
try
@@ -719,6 +700,7 @@ private static void SanitizeEntityMetadata(Entity entity)
719700
/// <summary>
720701
/// Sanitizes the annotations in an entity
721702
/// </summary>
703+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Entity.Annotations property is preserved by X-Ray SDK")]
722704
private static void SanitizeEntityAnnotations(Entity entity)
723705
{
724706
try
@@ -748,6 +730,7 @@ private static void SanitizeEntityAnnotations(Entity entity)
748730
/// <summary>
749731
/// Sanitizes HTTP information in an entity
750732
/// </summary>
733+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "Entity.Http property is preserved by X-Ray SDK")]
751734
private static void SanitizeEntityHttpInformation(Entity entity)
752735
{
753736
try
@@ -774,63 +757,5 @@ private static void SanitizeEntityHttpInformation(Entity entity)
774757
}
775758
}
776759

777-
/// <summary>
778-
/// Sanitizes other properties in an entity that might contain problematic data
779-
/// </summary>
780-
private static void SanitizeEntityOtherProperties(Entity entity)
781-
{
782-
try
783-
{
784-
// Get all properties of the entity
785-
var properties = entity.GetType()
786-
.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
787-
788-
foreach (var property in properties)
789-
{
790-
try
791-
{
792-
// Skip properties we've already handled
793-
if (property.Name == "Metadata" || property.Name == "Annotations" || property.Name == "Http")
794-
continue;
795-
796-
// Skip properties that can't be written to
797-
if (!property.CanWrite || !property.CanRead)
798-
continue;
799-
800-
// Skip indexers
801-
if (property.GetIndexParameters().Length > 0)
802-
continue;
803-
804-
var value = property.GetValue(entity);
805-
if (value == null)
806-
continue;
807-
808-
var valueType = value.GetType();
809760

810-
// Only sanitize properties that might contain problematic data
811-
if (NeedsTypeSanitization(valueType) ||
812-
valueType.IsClass && valueType != typeof(string) &&
813-
!valueType.IsPrimitive && !valueType.IsEnum)
814-
{
815-
var sanitizedValue = SanitizeValueForMetadata(value);
816-
817-
// Only update if the sanitized value is different and compatible
818-
if (!ReferenceEquals(value, sanitizedValue) &&
819-
(sanitizedValue == null || property.PropertyType.IsInstanceOfType(sanitizedValue)))
820-
{
821-
property.SetValue(entity, sanitizedValue);
822-
}
823-
}
824-
}
825-
catch (Exception ex)
826-
{
827-
Console.WriteLine($"Warning: Failed to sanitize property {property.Name}: {ex.Message}");
828-
}
829-
}
830-
}
831-
catch (Exception ex)
832-
{
833-
Console.WriteLine($"Warning: Other properties sanitization failed: {ex.Message}");
834-
}
835-
}
836761
}

0 commit comments

Comments
 (0)