// 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;
}
}
}
}