Skip to content

Commit

Permalink
Allow ElasticApmStartupHook to work with System.Diagnostics.Diagnosti…
Browse files Browse the repository at this point in the history
…cSource 4 and 5 (#1138)

Allow ElasticApmStartupHook to work with System.Diagnostics.DiagnosticSource 4 and 5

This commit updates the startup hook implementation to allow
it to work with applications that are compiled against either
System.Diagnostics.DiagnosticSource 4 or 5.

Introduce a `DiagnosticSourceVersion` MSBuild property to
Elastic.Apm to conditionally reference a different version of
System.Diagnostics.DiagnosticSource. The build agent-zip task 
uses this property to compile Elastic.Apm against both 
System.Diagnostics.DiagnosticSource 4.x.x and 5.x.x, and package
both into the startup hook zip file, under directories 4.0.0 and
5.0.0, respectively.

Split the startup hook agent assembly and dependency loading
into its own assembly, Elastic.Apm.StartupHook.Loader.
The Elastic.Apm.StartupHook.Loader takes a reference on Elastic.Apm, allowing
different versions to be compiled for different referenced
System.Diagnostics.DiagnosticSource versions.

The role of ElasticApmStartupHook is now to determine if a version of
System.Diagnostics.DiagnosticSource is already loaded or which version 
will be loaded, using this information to determine which version 
of Elastic.Apm.StartupHook.Loader to load and invoke.

Other changes:

* Update the build project pack target to
  build nuget packages and the zip file for startup hooks.

  The CI linux scripts are updated to use the build project
  pack target, requiring them to look in one directory
  for nuget packages, and allowing them to artifact the
  zip file.

* Rename ElasticApmStartupHook to ElasticApmAgentStartupHook

* Don't build .NET Framework in Release configuration for now

  Hanging on CI - last successful runs did not build with
  Release configuration, so running without for now.
  • Loading branch information
russcam authored Jan 27, 2021
1 parent d4e9e85 commit 914d301
Show file tree
Hide file tree
Showing 33 changed files with 620 additions and 218 deletions.
9 changes: 2 additions & 7 deletions .ci/linux/build.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
#!/usr/bin/env bash
#
# This script runs the dotnet build without the sample projects
#
set -euxo pipefail

# Remove Full Framework projects
.ci/linux/remove-projects.sh
set -euxo pipefail

dotnet build
./build.sh
16 changes: 14 additions & 2 deletions .ci/linux/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,23 @@
#
set -euo pipefail

declare -a projectsToPublish=("Elastic.Apm" "Elastic.Apm.AspNetCore" "Elastic.Apm.EntityFrameworkCore" "Elastic.Apm.NetCoreAll" "Elastic.Apm.EntityFramework6" "Elastic.Apm.AspNetFullFramework" "Elastic.Apm.SqlClient" "Elastic.Apm.Elasticsearch" "Elastic.Apm.Extensions.Hosting" "Elastic.Apm.GrpcClient")
# Packages that can be publicly released. A project may be marked as <IsPackable>true</IsPackable>
# to produce a nuget package for the CI feed, but it may not be ready for official release.
declare -a projectsToPublish=(
"Elastic.Apm"
"Elastic.Apm.AspNetCore"
"Elastic.Apm.EntityFrameworkCore"
"Elastic.Apm.NetCoreAll"
"Elastic.Apm.EntityFramework6"
"Elastic.Apm.AspNetFullFramework"
"Elastic.Apm.SqlClient"
"Elastic.Apm.Elasticsearch"
"Elastic.Apm.Extensions.Hosting"
"Elastic.Apm.GrpcClient")

for project in "${projectsToPublish[@]}"
do
for nupkg in $(find . -type f -not -path './.nuget/*' -name '*.nupkg')
for nupkg in $(find ./build/output/_packages -type f -name '*.nupkg')
do
pattern=".*${project}[0-9|.]*.nupkg"
if [[ $nupkg =~ $pattern ]]
Expand Down
15 changes: 4 additions & 11 deletions .ci/linux/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,16 @@
# sample projects.
#
# Parameters:
# - isVersionSuffixEnabled whether to add the --version-suffix with the
# current timestamp. Optional. Default false
# - isVersionSuffixEnabled whether to add the --canary flag. Optional. Default false
#
set -euxo pipefail

VERSION_SUFFIX_ENABLED=${1:-"false"}

# Set the version-suffix flag if required
# Set the canary flag if required
FLAG=''
if [ "${VERSION_SUFFIX_ENABLED}" = "true" ]; then
CURRENT_DATE=$(date -u +"%Y%m%d-%H%M%S")
FLAG="--version-suffix alpha-${CURRENT_DATE}"
FLAG='--canary'
fi

# Remove sample projects - and other we don't want to pack
dotnet sln remove test/Elastic.Apm.Benchmarks/Elastic.Apm.Benchmarks.csproj
dotnet sln remove test/Elastic.Apm.AspNetFullFramework.Tests/Elastic.Apm.AspNetFullFramework.Tests.csproj
dotnet sln remove sample/AspNetFullFrameworkSampleApp/AspNetFullFrameworkSampleApp.csproj
# shellcheck disable=SC2086
dotnet pack ${FLAG} -c Release
./build.sh pack ${FLAG}
2 changes: 1 addition & 1 deletion .ci/windows/msbuild.bat
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ echo "Prepare context for VsDevCmd.bat"
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\Tools\VsDevCmd.bat"
nuget restore -verbosity detailed -NonInteractive

msbuild /p:Configuration=Release
msbuild
2 changes: 1 addition & 1 deletion .ci/windows/test-iis.bat
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ if not exist "%sample_app_log_dir%" mkdir "%sample_app_log_dir%"
icacls %sample_app_log_dir% /t /grant Everyone:F
set ELASTIC_APM_ASP_NET_FULL_FRAMEWORK_SAMPLE_APP_LOG_FILE=%sample_app_log_dir%\Elastic.Apm.AspNetFullFramework.Tests.SampleApp.log

dotnet test -c Release test\Elastic.Apm.AspNetFullFramework.Tests --no-build ^
dotnet test test\Elastic.Apm.AspNetFullFramework.Tests --no-build ^
--verbosity normal ^
--results-directory target ^
--diag target\diag-iis.log ^
Expand Down
4 changes: 2 additions & 2 deletions .ci/windows/test-tools.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
# Install tools
dotnet tool install -g Codecov.Tool --version 1.2.0

Get-ChildItem -Path . -Recurse -Filter *.csproj |
# Add coverlet.msbuild only to test projects
Get-ChildItem -Path ./test -Recurse -Filter *.csproj |
Foreach-Object {
dotnet add $_.FullName package JunitXml.TestLogger --version 2.1.78
dotnet add $_.FullName package coverlet.msbuild --version 2.9.0
}
2 changes: 1 addition & 1 deletion .ci/windows/testnet461.bat
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
:: This script runs the tests and stored them in an xml file defined in the
:: LogFilePath property
::
dotnet publish -c Release test\Elastic.Apm.Tests --framework net461 -o outtestnet461
dotnet publish test\Elastic.Apm.Tests --framework net461 -o outtestnet461

dotnet vstest outtestnet461\Elastic.Apm.Tests.dll ^
--logger:"junit;LogFilePath=test\junit-{framework}-{assembly}.xml;MethodFormat=Class;FailureBodyFormat=Verbose"
9 changes: 8 additions & 1 deletion ElasticApmAgent.sln
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elastic.Apm.SqlClient.Tests
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elastic.Apm.Feature.Tests", "test\Elastic.Apm.Feature.Tests\Elastic.Apm.Feature.Tests.csproj", "{A4EF875C-92AD-4538-AFF1-1DA277F1085A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElasticApmStartupHook", "src\ElasticApmStartupHook\ElasticApmStartupHook.csproj", "{7B18C4C5-099B-41D1-8CC2-2D2C412B80CD}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ElasticApmAgentStartupHook", "src\ElasticApmAgentStartupHook\ElasticApmAgentStartupHook.csproj", "{7B18C4C5-099B-41D1-8CC2-2D2C412B80CD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elastic.Apm.Extensions.Hosting", "src\Elastic.Apm.Extensions.Hosting\Elastic.Apm.Extensions.Hosting.csproj", "{9BA642D5-2C8A-4B5E-AA75-50A4C29B0CCC}"
EndProject
Expand Down Expand Up @@ -117,6 +117,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Apm.AspNetCore.Stat
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Apm.Tests.Utilities", "test\Elastic.Apm.Tests.Utilities\Elastic.Apm.Tests.Utilities.csproj", "{43F9247D-544B-49FB-9E50-FC236D90DE1A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Apm.StartupHook.Loader", "src\Elastic.Apm.StartupHook.Loader\Elastic.Apm.StartupHook.Loader.csproj", "{B5200DC5-5E48-4F72-ADA9-96D0380A00CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Apm.StaticImplicitInitialization.Tests", "test\Elastic.Apm.StaticImplicitInitialization.Tests\Elastic.Apm.StaticImplicitInitialization.Tests.csproj", "{CE0120A5-A094-40A3-905E-8E14526800D3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Apm.StaticExplicitInitialization.Tests", "test\Elastic.Apm.StaticExplicitInitialization.Tests\Elastic.Apm.StaticExplicitInitialization.Tests.csproj", "{DC879845-353B-4C62-9640-6EA3008326F7}"
Expand Down Expand Up @@ -286,6 +288,10 @@ Global
{43F9247D-544B-49FB-9E50-FC236D90DE1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43F9247D-544B-49FB-9E50-FC236D90DE1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43F9247D-544B-49FB-9E50-FC236D90DE1A}.Release|Any CPU.Build.0 = Release|Any CPU
{B5200DC5-5E48-4F72-ADA9-96D0380A00CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5200DC5-5E48-4F72-ADA9-96D0380A00CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5200DC5-5E48-4F72-ADA9-96D0380A00CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5200DC5-5E48-4F72-ADA9-96D0380A00CB}.Release|Any CPU.Build.0 = Release|Any CPU
{CE0120A5-A094-40A3-905E-8E14526800D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CE0120A5-A094-40A3-905E-8E14526800D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CE0120A5-A094-40A3-905E-8E14526800D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -338,6 +344,7 @@ Global
{5D076C7F-1F8B-4B11-9910-48717D133963} = {3734A52F-2222-454B-BF58-1BA5C1F29D77}
{2250D888-E4CC-4B2B-AF31-5C78D76EC73D} = {267A241E-571F-458F-B04C-B6C4DE79E735}
{43F9247D-544B-49FB-9E50-FC236D90DE1A} = {267A241E-571F-458F-B04C-B6C4DE79E735}
{B5200DC5-5E48-4F72-ADA9-96D0380A00CB} = {3734A52F-2222-454B-BF58-1BA5C1F29D77}
{CE0120A5-A094-40A3-905E-8E14526800D3} = {267A241E-571F-458F-B04C-B6C4DE79E735}
{DC879845-353B-4C62-9640-6EA3008326F7} = {267A241E-571F-458F-B04C-B6C4DE79E735}
EndGlobalSection
Expand Down
4 changes: 2 additions & 2 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ pipeline {
}
success {
whenTrue(isPR()) {
archiveArtifacts(allowEmptyArchive: true, artifacts: "${BASE_DIR}/**/bin/Release/**/*.nupkg")
archiveArtifacts(allowEmptyArchive: true, artifacts: "${BASE_DIR}/build/output/_packages/*.nupkg,${BASE_DIR}/build/output/*.zip")
}
}
}
Expand Down Expand Up @@ -406,7 +406,7 @@ pipeline {
post{
success {
archiveArtifacts(allowEmptyArchive: true,
artifacts: "${BASE_DIR}/**/bin/Release/**/*.nupkg")
artifacts: "${BASE_DIR}/build/output/_packages/*.nupkg")
}
}
}
Expand Down
Empty file modified build.sh
100644 → 100755
Empty file.
12 changes: 12 additions & 0 deletions build/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Builds a docker image containing all of the assemblies needed to
# auto instrument the APM agent using startup hooks.
#
# Assumes that the agent

FROM busybox

RUN mkdir /usr/agent

# Assumes that the agent directory has been built.
# Run build.[bat|sh] agent-zip to build it
COPY . /usr/agent
153 changes: 125 additions & 28 deletions build/scripts/Build.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,30 @@
namespace Scripts

open System
open System.Collections.Generic
open System.IO
open System.IO.Compression
open System.Linq
open System.Runtime.InteropServices
open System.Xml.Linq
open Buildalyzer
open Fake.Core
open Fake.DotNet
open Fake.IO
open Fake.IO.Globbing.Operators
open Tooling

module Build =
module Build =

let private oldDiagnosticSourceVersion = SemVer.parse "4.6.0"

let mutable private currentDiagnosticSourceVersion = None

let private isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)

let private aspNetFullFramework = Paths.SrcProjFile "Elastic.Apm.AspNetFullFramework"

let private allSrcProjectsExceptFullFramework =
!! "src/**/*.csproj"
-- "src/**/Elastic.Apm.AspNetFullFramework.csproj"
let private allSrcProjects = !! "src/**/*.csproj"

let private fullFrameworkProjects = [
aspNetFullFramework
Expand All @@ -49,17 +55,19 @@ module Build =

/// Copy all the bin release outputs to the build/output directory
let private copyBinRelease () =
allSrcProjectsExceptFullFramework
allSrcProjects
|> Seq.iter(fun p ->
let directory = Path.GetDirectoryName p
let project = Path.GetFileNameWithoutExtension p
let bin = Path.combine directory "bin/Release"
let buildOutput = Paths.BuildOutput project
Shell.copyDir buildOutput bin (fun _ -> true)

if Directory.Exists bin then
let buildOutput = Paths.BuildOutput project
Shell.copyDir buildOutput bin (fun _ -> true)
)

let private dotnet target projectOrSln =
DotNet.Exec [target |> String.toLower ; projectOrSln; "-c"; "Release"; "-v"; "q"; "--nologo"]
DotNet.Exec [target; projectOrSln; "-c"; "Release"; "-v"; "q"; "--nologo"]

let private msBuild target projectOrSln =
MSBuild.build (fun p -> {
Expand All @@ -75,7 +83,50 @@ module Build =
DisableInternalBinLog = true
NoLogo = true
}) projectOrSln


/// Gets the current version of System.Diagnostics.DiagnosticSource referenced by Elastic.Apm
let private getCurrentApmDiagnosticSourceVersion =
match currentDiagnosticSourceVersion with
| Some v -> v
| None ->
let manager = AnalyzerManager();
let analyzer = manager.GetProject(Paths.SrcProjFile "Elastic.Apm")
let analyzeResult = analyzer.Build("netstandard2.0").First()
let values = analyzeResult.PackageReferences.["System.Diagnostics.DiagnosticSource"]
let version = SemVer.parse values.["Version"]
currentDiagnosticSourceVersion <- Some(version)
version

let private majorVersions = Dictionary<SemVerInfo, SemVerInfo>()

/// Publishes ElasticApmStartupHook against a 4.x version of System.Diagnostics.DiagnosticSource
let private publishElasticApmStartupHookWithDiagnosticSourceVersion () =
let projects =
!! (Paths.SrcProjFile "Elastic.Apm")
++ (Paths.SrcProjFile "Elastic.Apm.StartupHook.Loader")

projects
|> Seq.map getAllTargetFrameworks
|> Seq.iter (fun (proj, frameworks) ->
frameworks
|> Seq.iter(fun framework ->
let output =
Path.GetFileNameWithoutExtension proj
|> (fun p -> sprintf "%s_%i.0.0/%s" p oldDiagnosticSourceVersion.Major framework)
|> Paths.BuildOutput
|> Path.GetFullPath

printfn "Publishing %s %s with System.Diagnostics.DiagnosticSource %O..." proj framework oldDiagnosticSourceVersion
DotNet.Exec ["publish" ; proj
sprintf "\"/p:DiagnosticSourceVersion=%O\"" oldDiagnosticSourceVersion
"-c"; "Release"
"-f"; framework
"-v"; "q"
"-o"; output
"--nologo"; "--force"]
)
)

/// Generates a new .sln file that contains only .NET Core projects
let GenerateNetCoreSln () =
File.Copy(Paths.Solution, Paths.SolutionNetCore, true)
Expand All @@ -88,26 +139,41 @@ module Build =
dotnet "build" Paths.SolutionNetCore
if isWindows then msBuild "Build" aspNetFullFramework
copyBinRelease()

/// Publishes all projects with framework versions
let Publish () =
allSrcProjectsExceptFullFramework
let Publish targets =

let projs =
match targets with
| Some t -> t
| None -> allSrcProjects

projs
|> Seq.map getAllTargetFrameworks
|> Seq.iter (fun (proj, frameworks) ->
frameworks
|> Seq.iter(fun framework ->
let output =
Path.GetFileNameWithoutExtension proj
|> (fun p -> sprintf "%s/%s" p framework)
|> Paths.BuildOutput
|> Path.GetFullPath

printfn "Publishing %s %s..." proj framework
DotNet.Exec ["publish" ; proj; "-c"; "Release"; "-f"; framework; "-v"; "q"; "--nologo"]
DotNet.Exec ["publish" ; proj; "-c"; "Release"; "-f"; framework; "-v"; "q"; "--nologo"; "-o"; output]
)
)

copyBinRelease()

publishElasticApmStartupHookWithDiagnosticSourceVersion()

/// Version suffix used for canary builds
let versionSuffix = DateTime.UtcNow.ToString("yyyyMMdd-HHmmss") |> sprintf "alpha-%s"

/// Packages projects into nuget packages
let Pack (canary:bool) =
let arguments =
let a = ["pack" ; Paths.SolutionNetCore; "-c"; "Release"; "-o"; Paths.NugetOutput]
if canary then List.append a ["--version-suffix"; DateTime.UtcNow.ToString("yyyyMMdd-HHmmss") |> sprintf "alpha-%s"]
let a = ["pack" ; Paths.Solution; "-c"; "Release"; "-o"; Paths.NugetOutput]
if canary then List.append a ["--version-suffix"; versionSuffix]
else a
DotNet.Exec arguments

Expand All @@ -122,23 +188,54 @@ module Build =
if isWindows then DotNet.Exec ["restore" ; aspNetFullFramework; "-v"; "q"]

/// Creates versioned ElasticApmAgent.zip file
let AgentZip () =
let name = sprintf "ElasticApmAgent_%s" (Versioning.CurrentVersion.AssemblyVersion.ToString())
let AgentZip (canary:bool) =
let name = "ElasticApmAgent"
let versionedName =
if canary then
sprintf "%s_%s-%s" name (Versioning.CurrentVersion.AssemblyVersion.ToString()) versionSuffix
else
sprintf "%s_%s" name (Versioning.CurrentVersion.AssemblyVersion.ToString())

let agentDir = Paths.BuildOutput name |> DirectoryInfo
agentDir.Create()

let rec copyRecursive (destination: DirectoryInfo) (source: DirectoryInfo) =
source.GetDirectories()
|> Seq.iter (fun dir -> copyRecursive (destination.CreateSubdirectory dir.Name) dir)
// all files of interest are top level files in the source directory
let copy (destination: DirectoryInfo) (source: DirectoryInfo) =
source.GetFiles()
|> Seq.filter (fun file -> file.Extension = ".dll" || file.Extension = ".pdb")
|> Seq.iter (fun file -> file.CopyTo(Path.combine destination.FullName file.Name, true) |> ignore)

let copyToAgentDir = copyRecursive agentDir

!! (Paths.BuildOutput "**/netstandard2.0/publish")
++ (Paths.BuildOutput "ElasticApmStartupHook/netcoreapp2.2/publish")
// copy startup hook to root of agent directory
!! (Paths.BuildOutput "ElasticApmAgentStartupHook/netcoreapp2.2")
|> Seq.filter Path.isDirectory
|> Seq.map DirectoryInfo
|> Seq.iter (copy agentDir)

// assemblies compiled against "current" version of System.Diagnostics.DiagnosticSource
!! (Paths.BuildOutput "Elastic.Apm.StartupHook.Loader/netcoreapp2.2")
++ (Paths.BuildOutput "Elastic.Apm/netstandard2.0")
|> Seq.filter Path.isDirectory
|> Seq.map DirectoryInfo
|> Seq.iter (copy (agentDir.CreateSubdirectory(sprintf "%i.0.0" getCurrentApmDiagnosticSourceVersion.Major)))

// assemblies compiled against older version of System.Diagnostics.DiagnosticSource
!! (Paths.BuildOutput (sprintf "Elastic.Apm.StartupHook.Loader_%i.0.0/netcoreapp2.2" oldDiagnosticSourceVersion.Major))
++ (Paths.BuildOutput (sprintf "Elastic.Apm_%i.0.0/netstandard2.0" oldDiagnosticSourceVersion.Major))
|> Seq.filter Path.isDirectory
|> Seq.map DirectoryInfo
|> Seq.iter copyToAgentDir
|> Seq.iter (copy (agentDir.CreateSubdirectory(sprintf "%i.0.0" oldDiagnosticSourceVersion.Major)))

ZipFile.CreateFromDirectory(agentDir.FullName, Paths.BuildOutput name + ".zip")
// include version in the zip file name
ZipFile.CreateFromDirectory(agentDir.FullName, Paths.BuildOutput versionedName + ".zip")


/// Builds docker image including the ElasticApmAgent
let AgentDocker (canary:bool) =
let agentVersion =
if canary then
sprintf "%s-%s" (Versioning.CurrentVersion.AssemblyVersion.ToString()) versionSuffix
else
Versioning.CurrentVersion.AssemblyVersion.ToString()

Docker.Exec [ "build"; "--file"; "./build/docker/Dockerfile";
"--tag"; sprintf "observability/apm-agent-dotnet:%s" agentVersion; "./build/output/ElasticApmAgent" ]
Loading

0 comments on commit 914d301

Please sign in to comment.