// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; namespace UnrealBuildTool { /// /// Telemetry service to collect data about execution. /// /// /// You must provide an instance of a class that implements and has the attribute. /// Epic provides one internally, but external studios will need to provide one themselves. /// public static class Telemetry { /// /// Provides an interface for a telemetry provider. /// public interface IProvider : IDisposable { /// /// Sends an event to the telemetry provider /// /// Name of the event /// list of attributes in key,value format. void SendEvent(string EventName, IEnumerable> Attributes); } /// /// Attribute to designate a telemetry provider /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class ProviderAttribute : Attribute { } /// /// Get all assemblies in this domain which reference the passed-in assembly /// /// The domain to search /// The assembly to search for references to /// All Assemblies that reference target. private static IEnumerable GetReferencingAssemblies(this AppDomain domain, Assembly target) { // return the target assembly and any assemblies in the domain that reference the target assembly. return domain.GetAssemblies().Where(asm => asm.GetReferencedAssemblies().Any(refd => refd.FullName == target.FullName)).Union(new[] { target }); } static IProvider Provider; /// /// Called at app startup to initialize the telemetry provider. /// public static void Initialize() { var TelemetryInitStartTime = DateTime.UtcNow; // reflect over the assembly and find the first eligible provider class. Provider = (from reflectionAssembly in AppDomain.CurrentDomain.GetReferencingAssemblies(Assembly.GetExecutingAssembly()) from T in reflectionAssembly.GetTypes() // type must implement interface, contain the attribute, be concrete and default constructible. where T.IsDefined(typeof(ProviderAttribute), true) && T.GetInterfaces().Contains(typeof(IProvider)) && T.IsClass && !T.IsAbstract && T.GetConstructor(Type.EmptyTypes) != null // create the instance select (IProvider)Activator.CreateInstance(T)).FirstOrDefault(); if( BuildConfiguration.bPrintPerformanceInfo ) { var TelemetryInitTime = (DateTime.UtcNow - TelemetryInitStartTime).TotalSeconds; Log.TraceInformation( "Telemetry initialization took " + TelemetryInitTime + "s" ); } } /// /// Checks if the Telemetry systeme is available. Use to skip work to prepare telemetry events. /// /// If unavailable, SendEvent will not crash, but will ignore the event. /// public static bool IsAvailable() { return Provider != null; } /// /// Helper to allow one to pass a series of key/value pairs as a single string list of the form: key1,value1,key2,value2,... /// Odd lengths are truncated. Pass an empty value using and empty string or null. /// /// /// public static void SendEvent(string EventName, params string[] Attributes) { if (Provider == null) return; // zip even values as attribute names, odd values as attribute values. SendEvent(EventName, Attributes.Where((value, index) => (index % 2) == 0).Zip(Attributes.Where((value, index) => (index % 2) == 1), (key, value) => Tuple.Create(key, value))); } public static void SendEvent(string EventName, IEnumerable> Attributes) { if (Provider == null) return; Provider.SendEvent(EventName, Attributes); } /// /// Called at app shutdown to clean up the telemetry provider. /// public static void Shutdown() { if (Provider != null) { Provider.Dispose(); Provider = null; } } } }