Example #1
0
    /// <summary>
    /// Invokes the Elastic.Apm.StartupHook.Loader.Loader.Initialize() method from a specific assembly
    /// </summary>
    /// <param name="loaderAssembly">The loader assembly</param>
    private static void InvokerLoaderMethod(Assembly loaderAssembly)
    {
        if (loaderAssembly is null)
        {
            return;
        }

        const string loaderTypeName   = "Elastic.Apm.StartupHook.Loader.Loader";
        const string loaderTypeMethod = "Initialize";

        _logger.WriteLine($"Get {loaderTypeName} type");
        var loaderType = loaderAssembly.GetType(loaderTypeName);

        if (loaderType is null)
        {
            _logger.WriteLine($"{loaderTypeName} type is null");
            return;
        }

        _logger.WriteLine($"Get {loaderTypeName}.{loaderTypeMethod} method");
        var initializeMethod = loaderType.GetMethod(loaderTypeMethod, BindingFlags.Public | BindingFlags.Static);

        if (initializeMethod is null)
        {
            _logger.WriteLine($"{loaderTypeName}.{loaderTypeMethod} method is null");
            return;
        }

        _logger.WriteLine($"Invoke {loaderTypeName}.{loaderTypeMethod} method");
        initializeMethod.Invoke(null, null);
    }
Example #2
0
 static void LoadDiagnosticSubscriber(IDiagnosticsSubscriber diagnosticsSubscriber, StartupHookLogger logger)
 {
     try
     {
         Agent.Subscribe(diagnosticsSubscriber);
     }
     catch (Exception e)
     {
         logger.WriteLine($"Failed subscribing to {diagnosticsSubscriber.GetType().Name}, " +
                          $"Exception type: {e.GetType().Name}, message: {e.Message}");
     }
 }
Example #3
0
    /// <summary>
    /// Loads assemblies from the loader directory if they exist
    /// </summary>
    /// <param name="loaderDirectory"></param>
    private static void LoadAssembliesFromLoaderDirectory(string loaderDirectory)
    {
        var context = new ElasticApmAssemblyLoadContext();

        AssemblyLoadContext.Default.Resolving += (_, name) =>
        {
            var assemblyPath = Path.Combine(loaderDirectory, name.Name + ".dll");
            if (File.Exists(assemblyPath))
            {
                try
                {
                    var assemblyName = AssemblyName.GetAssemblyName(assemblyPath);
                    if (name.Version == assemblyName.Version)
                    {
                        var keyToken         = name.GetPublicKeyToken();
                        var assemblyKeyToken = assemblyName.GetPublicKeyToken();
                        if (keyToken.SequenceEqual(assemblyKeyToken))
                        {
                            // load Elastic.Apm assemblies with the default assembly load context, to allow DiagnosticListeners to subscribe.
                            // For all other dependencies, load with a separate load context to not conflict with application dependencies.
                            return(name.Name.StartsWith("Elastic.Apm", StringComparison.Ordinal)
                                                                ? AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath)
                                                                : context.LoadFromAssemblyPath(assemblyPath));
                        }
                    }
                }
                catch (Exception e)
                {
                    _logger.WriteLine(e.ToString());
                }
            }

            return(null);
        };
    }
Example #4
0
    /// <summary>
    /// The Initialize method called by DOTNET_STARTUP_HOOKS
    /// </summary>
    public static void Initialize()
    {
        var startupHookEnvVar    = Environment.GetEnvironmentVariable("DOTNET_STARTUP_HOOKS");
        var startupHookDirectory = Path.GetDirectoryName(startupHookEnvVar);

        if (string.IsNullOrEmpty(startupHookEnvVar) || !File.Exists(startupHookEnvVar))
        {
            return;
        }

        _logger = StartupHookLogger.Create();
        _logger.WriteLine($"Check if {SystemDiagnosticsDiagnosticsource} is loaded");

        var assemblies = AppDomain.CurrentDomain.GetAssemblies();

        _logger.WriteLine($"Assemblies loaded:{Environment.NewLine}{string.Join(Environment.NewLine, assemblies.Select(a => a.GetName()))}");

        var diagnosticSourceAssemblies = assemblies
                                         .Where(a => a.GetName().Name.Equals(SystemDiagnosticsDiagnosticsource, StringComparison.Ordinal))
                                         .ToList();

        Assembly diagnosticSourceAssembly;

        switch (diagnosticSourceAssemblies.Count)
        {
        case 0:
            _logger.WriteLine($"No {SystemDiagnosticsDiagnosticsource} loaded. Load using AssemblyLoadContext.Default");

            diagnosticSourceAssembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(SystemDiagnosticsDiagnosticsource));
            break;

        case 1:
            diagnosticSourceAssembly = diagnosticSourceAssemblies[0];
            break;

        default:
            _logger.WriteLine($"Found {diagnosticSourceAssemblies.Count} {SystemDiagnosticsDiagnosticsource} assemblies loaded in the app domain");
            diagnosticSourceAssembly = diagnosticSourceAssemblies.First();
            break;
        }

        Assembly loaderAssembly = null;
        string   loaderDirectory;

        if (diagnosticSourceAssembly is null)
        {
            // use agent compiled against the highest version of System.Diagnostics.DiagnosticSource
            var highestAvailableAgent = Directory.EnumerateDirectories(startupHookDirectory)
                                        .Where(d => VersionRegex.IsMatch(d))
                                        .OrderByDescending(d => VersionRegex.Match(d).Groups["major"].Value)
                                        .First();

            loaderDirectory = Path.Combine(startupHookDirectory, highestAvailableAgent);
            loaderAssembly  = AssemblyLoadContext.Default
                              .LoadFromAssemblyPath(Path.Combine(loaderDirectory, LoaderDll));
        }
        else
        {
            var diagnosticSourceAssemblyName   = diagnosticSourceAssembly.GetName();
            var diagnosticSourcePublicKeyToken = diagnosticSourceAssemblyName.GetPublicKeyToken();
            if (!diagnosticSourcePublicKeyToken.SequenceEqual(SystemDiagnosticsDiagnosticSourcePublicKeyToken))
            {
                _logger.WriteLine($"{SystemDiagnosticsDiagnosticsource} public key token "
                                  + $"{PublicKeyTokenBytesToString(diagnosticSourcePublicKeyToken)} did not match expected "
                                  + $"public key token {PublicKeyTokenBytesToString(SystemDiagnosticsDiagnosticSourcePublicKeyToken)}");
                return;
            }

            var diagnosticSourceVersion = diagnosticSourceAssemblyName.Version;
            _logger.WriteLine($"{SystemDiagnosticsDiagnosticsource} {diagnosticSourceVersion} loaded");

            loaderDirectory = Path.Combine(startupHookDirectory, $"{diagnosticSourceVersion.Major}.0.0");
            if (Directory.Exists(loaderDirectory))
            {
                loaderAssembly = AssemblyLoadContext.Default
                                 .LoadFromAssemblyPath(Path.Combine(loaderDirectory, LoaderDll));
            }
            else
            {
                _logger.WriteLine(
                    $"No compatible agent for {SystemDiagnosticsDiagnosticsource} {diagnosticSourceVersion}. Agent not loaded");
            }
        }

        if (loaderAssembly is null)
        {
            _logger.WriteLine(
                $"No {LoaderDll} assembly loaded. Agent not loaded");
        }

        LoadAssembliesFromLoaderDirectory(loaderDirectory);
        InvokerLoaderMethod(loaderAssembly);
    }