sample/ApiSamples/Program.cs (243 lines of code) (raw):
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using Elastic.Apm;
using Elastic.Apm.Api;
namespace ApiSamples
{
/// <summary>
/// This class exercises the Public Agent API.
/// </summary>
internal class Program
{
private static void Main(string[] args)
{
Environment.SetEnvironmentVariable("ELASTIC_APM_LOG_LEVEL", "Trace");
CompressionSample();
Console.ReadKey();
if (args.Length == 1) //in case it's started with arguments, parse DistributedTracingData from them
{
var distributedTracingData = DistributedTracingData.TryDeserializeFromString(args[0]);
WriteLineToConsole($"Callee process started - continuing trace with distributed tracing data: {distributedTracingData}");
var transaction2 = Agent.Tracer.StartTransaction("Transaction2", "TestTransaction", distributedTracingData);
try
{
transaction2.CaptureSpan("TestSpan", "TestSpanType", () => Thread.Sleep(200));
}
finally
{
transaction2.End();
}
Thread.Sleep(TimeSpan.FromSeconds(11));
WriteLineToConsole("About to exit");
}
else
{
WriteLineToConsole("Started");
PassDistributedTracingData();
//WIP: if the process terminates the agent
//potentially does not have time to send the transaction to the server.
Thread.Sleep(TimeSpan.FromSeconds(11));
WriteLineToConsole("About to exit - press any key...");
Console.ReadKey();
}
}
public static void CompressionSample()
{
Environment.SetEnvironmentVariable("ELASTIC_APM_SPAN_COMPRESSION_ENABLED", "true");
Environment.SetEnvironmentVariable("ELASTIC_APM_SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION", "1s");
Agent.Tracer.CaptureTransaction("Foo", "Bar", t =>
{
t.CaptureSpan("foo1", "bar", span1 =>
{
for (var i = 0; i < 10; i++)
{
span1.CaptureSpan("Select * From Table1", ApiConstants.TypeDb, (s) =>
{
s.Context.Db = new Database() { Type = "mssql", Instance = "01" };
}, ApiConstants.SubtypeMssql, isExitSpan: true);
}
span1.CaptureSpan("foo2", "randomSpan", () => { });
for (var i = 0; i < 10; i++)
{
span1.CaptureSpan("Select * From Table2", ApiConstants.TypeDb, (s2) =>
{
s2.Context.Db = new Database() { Type = "mssql", Instance = "01" };
}, ApiConstants.SubtypeMssql, isExitSpan: true);
}
});
});
}
public static void SampleCustomTransaction()
{
WriteLineToConsole($"{nameof(SampleCustomTransaction)} started");
var transaction = Agent.Tracer.StartTransaction("SampleTransaction", ApiConstants.TypeRequest);
Thread.Sleep(500); //simulate work...
transaction.End();
WriteLineToConsole($"{nameof(SampleCustomTransaction)} finished");
}
public static void SampleCustomTransactionWithSpan()
{
WriteLineToConsole($"{nameof(SampleCustomTransactionWithSpan)} started");
var transaction = Agent.Tracer.StartTransaction("SampleTransactionWithSpan", ApiConstants.TypeRequest);
Thread.Sleep(500);
var span = transaction.StartSpan("SampleSpan", ApiConstants.TypeExternal);
Thread.Sleep(200);
span.End();
transaction.End();
WriteLineToConsole($"{nameof(SampleCustomTransactionWithSpan)} finished");
}
public static void SampleError()
{
WriteLineToConsole($"{nameof(SampleError)} started");
var transaction = Agent.Tracer.StartTransaction("SampleError", ApiConstants.TypeRequest);
Thread.Sleep(500); //simulate work...
var span = transaction.StartSpan("SampleSpan", ApiConstants.TypeExternal);
try
{
throw new Exception("bamm");
}
catch (Exception e)
{
span.CaptureException(e);
}
finally
{
span.End();
}
transaction.End();
WriteLineToConsole($"{nameof(SampleError)} finished");
}
public static void SampleCustomTransactionWithConvenientApi() => Agent.Tracer.CaptureTransaction("TestTransaction", "TestType",
t =>
{
t.Context.Response = new Response { Finished = true, StatusCode = 200 };
t.Context.Request = new Request("GET", new Url { Protocol = "HTTP" });
t.SetLabel("fooTransaction1", "barTransaction1");
t.SetLabel("fooTransaction2", "barTransaction2");
Thread.Sleep(10);
t.CaptureSpan("TestSpan", "TestSpanType", s =>
{
Thread.Sleep(20);
s.SetLabel("fooSpan", "barSpan");
});
});
//1 transaction with 2 spans
//1 transaction with 1 span that has a sub span
public static void TwoTransactionWith2Spans()
{
//1 transaction 2 spans (both have the transaction as parent)
Agent.Tracer.CaptureTransaction("TestTransaction1", "TestType1",
t =>
{
t.CaptureSpan("TestSpan", "TestSpanType", _ =>
{
Thread.Sleep(20);
//this span is also started on the transaction:
t.CaptureSpan("TestSpan2", "TestSpanType", _ => { Thread.Sleep(20); });
});
});
//1 transaction then 1 span on that transaction and then 1 span on that previous span
Agent.Tracer.CaptureTransaction("TestTransaction2", "TestType2",
t =>
{
t.CaptureSpan("TestSpan", "TestSpanType", s =>
{
Thread.Sleep(20);
//this span is a subspan of the `s` span:
s.CaptureSpan("TestSpan2", "TestSpanType", () => Thread.Sleep(20));
});
});
}
public static void PassDistributedTracingData()
{
var transaction = Agent.Tracer.StartTransaction("Transaction1", "TestTransaction");
try
{
Thread.Sleep(300);
//We start the sample app again with a new service name and we pass DistributedTracingData to it
//In the main method we check for this and continue the trace.
var distributedTracingData = Agent.Tracer.CurrentSpan?.OutgoingDistributedTracingData
?? Agent.Tracer.CurrentTransaction?.OutgoingDistributedTracingData;
var traceParent = distributedTracingData?.SerializeToString();
var assembly = Assembly.GetExecutingAssembly().Location;
WriteLineToConsole(
$"Spawning callee process and passing outgoing distributed tracing data: {traceParent} to it...");
var startInfo = new ProcessStartInfo { FileName = "dotnet", Arguments = $"{assembly} {traceParent}" };
startInfo.Environment["ELASTIC_APM_SERVICE_NAME"] = "Service2";
var calleeProcess = Process.Start(startInfo);
WriteLineToConsole("Spawned callee process");
Thread.Sleep(1100);
WriteLineToConsole("Waiting for callee process to exit...");
calleeProcess?.WaitForExit();
WriteLineToConsole("Callee process exited");
}
finally
{
transaction.End();
}
}
private static void WriteLineToConsole(string line) =>
Console.WriteLine($"[{Process.GetCurrentProcess().Id}] {line}");
/// <summary>
/// Registers some sample filters.
/// </summary>
// ReSharper disable once UnusedMember.Local
private static void FilterSample()
{
Agent.AddFilter((ITransaction transaction) =>
{
transaction.Name = "NewTransactionName";
return transaction;
});
Agent.AddFilter((ITransaction transaction) =>
{
transaction.Type = "NewSpanName";
return transaction;
});
Agent.AddFilter((ISpan span) =>
{
span.Name = "NewSpanName";
return span;
});
Agent.AddFilter(span =>
{
if (span.StackTrace.Count > 10)
span.StackTrace.RemoveRange(10, span.StackTrace.Count - 10);
return span;
});
Agent.AddFilter(error =>
{
if (error.Culprit == "SecretComponent")
return null;
if (error.Exception.Type == "SecretType")
error.Exception.Message = "[HIDDEN]";
Console.WriteLine(
$"Error printed in a filter - culprit: {error.Culprit}, id: {error.Id}, parentId: {error.ParentId}, traceId: {error.TraceId}, transactionId: {error.TransactionId}");
return error;
});
}
#pragma warning disable IDE0022
// ReSharper disable ArrangeMethodOrOperatorBody
public static void SampleSpanWithCustomContext()
{
Agent.Tracer.CaptureTransaction("SampleTransaction", "SampleTransactionType",
transaction =>
{
transaction.CaptureSpan("SampleSpan", "SampleSpanType",
span => { span.Context.Db = new Database { Statement = "GET /_all/_search?q=tag:wow", Type = Database.TypeElasticsearch }; });
});
}
public static void SampleSpanWithCustomContextFillAll()
{
Agent.Tracer.CaptureTransaction("SampleTransaction", "SampleTransactionType", transaction =>
{
transaction.CaptureSpan("SampleSpan1", "SampleSpanType", span =>
{
// ReSharper disable once UseObjectOrCollectionInitializer
#pragma warning disable IDE0017
span.Context.Http = new Http { Url = "http://mysite.com", Method = "GET" };
#pragma warning restore IDE0017
// send request, get response with status code
span.Context.Http.StatusCode = 200;
});
transaction.CaptureSpan("SampleSpan2", "SampleSpanType",
span =>
{
span.Context.Db = new Database
{
Statement = "GET /_all/_search?q=tag:wow",
Type = Database.TypeElasticsearch,
Instance = "MyInstance"
};
});
});
}
// ReSharper restore ArrangeMethodOrOperatorBody
#pragma warning restore IDE0022
#if NET8_0_OR_GREATER
/// <summary>
/// Test for https://github.com/elastic/apm-agent-dotnet/issues/884
/// </summary>
private IAsyncEnumerable<int> TestCompilation() => throw new Exception();
#endif
}
}