Last active
January 29, 2025 15:51
-
-
Save szalapski/406b0c9472985e2867179ff2fd0226de to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Extension methods for working with DataDog spans. | |
/// </summary> | |
/// <remarks>These could someday be made into a simple NuGet package for use anywhere.</remarks> | |
public static class DatadogExtensions | |
{ | |
/// <summary> | |
/// Sets a Datadog tag with a parsed complex value onto a span, from an XML string | |
/// </summary> | |
/// <remarks>If the XML string cannot be parsed as XML, the tag will still be added, but with "string" appended | |
/// to the key name.</remarks> | |
public static void SetTagFromXml(this ISpan span, string key, string valueXmlString) | |
{ | |
try | |
{ | |
XElement element = XElement.Parse(valueXmlString); | |
span.SetTagFromXElement(key, element); | |
} | |
catch (Exception e) | |
{ | |
span.SetTag($"{key}string", valueXmlString); | |
bool isReallyAnError = span.Error; | |
span.SetException(e); | |
span.Error = isReallyAnError; // make XML parsing errors not count as errors | |
} | |
} | |
/// <summary> | |
/// | |
/// </summary> | |
/// <param name="span"></param> | |
/// <param name="keyPrefix"></param> | |
/// <param name="element"></param> | |
/// <remarks>This parses only elements and texts from the XML; it could be further enhanced to capture comments, | |
/// attributes, etc.</remarks> | |
public static void SetTagFromXElement(this ISpan span, string keyPrefix, XElement? element) | |
{ | |
if (element is null) | |
{ | |
span.SetTag(keyPrefix.NormalizeTagKey(), ""); // note that DataDog doesn't allow an explicit null | |
return; | |
} | |
string name = $"{keyPrefix}.{element.Name.LocalName}"; | |
if (element.HasElements) | |
{ | |
// if there is both text and child elements, add a special "text_" child tag that is unlikely to be an | |
// actual child element. (In the very rare case that it is, the text node will be overwritten by the | |
// actual child element.) | |
string textValue = string.Concat(element.Nodes().OfType<XText>().Select(t => t.Value)); | |
if (!string.IsNullOrWhiteSpace(textValue)) span.SetTag($"{name.NormalizeTagKey()}.text_", textValue); | |
foreach (XElement child in element.Elements()) span.SetTagFromXElement(name, child); | |
} | |
else span.SetTag(name.NormalizeTagKey(), element.Value); | |
} | |
/// <summary> | |
/// Returns a key string that is changed to conform to DataDog's restrictions for tag key names, | |
/// per https://docs.datadoghq.com/getting_started/tagging/. | |
/// </summary> | |
public static string NormalizeTagKey(this string key) | |
{ | |
if (string.IsNullOrWhiteSpace(key)) | |
throw new ArgumentOutOfRangeException(nameof(key), "String value must not be null, empty, or whitespace."); | |
// Allow only alphanumerics, underscores, minuses, colons, periods, slashes; substitute a _ for all others | |
const string disallowedCharacters = @"[^\w\-:\./]"; | |
string result = Regex.Replace(key, disallowedCharacters, "_"); | |
// The first character is more restricted: it must be a letter, so prepend a "z" if it isn't | |
if (char.ToLowerInvariant(result[0]) is < 'a' or > 'z') result = $"z{result}"; | |
return result; | |
} | |
public static ISpan SetTagsRange(this ISpan span, string tag, NameValueCollection collection) | |
{ | |
foreach (string key in collection.AllKeys) span.SetTag($"{tag}.{key}", collection[key]); | |
return span; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment