Dotnet APM Agent Public API calls not cooperating

Kibana version: 6.8.0

Elasticsearch version: 6.8.0

APM Server version: 6.8.0

APM Agent language and version: dotnet 1.0.0-beta1

Browser version: Firefox Quantum 67.0.4 (64-bit)

Original install method: Download pages, all versions listed above.

Fresh install or upgraded from other version? Fresh installs.

Is there anything special in your setup? Everything is installed and accessed locally.

Description of the problem including expected versus actual behavior. Please include screenshots (if relevant):
My goal is to utilize the agent with non-ASP .net (i.e., non-web) C# applications, but I’m having difficulties getting the dotnet APM agent to talk to the APM server using the public API.

I have tried a few different approaches: straightforward public API calls (with configuration set via appsettings.json and/or Environment.SetEnvironmentVariable calls) do not seem to send entries to the APM server. Projects using the web ASP.net agent implementation (via the call in Startup.cs) do send entries to the APM server, including those from public API calls.

I tried the code RenauldGelai posted in an issue on the agent’s Github repo (https://github.com/elastic/apm-agent-dotnet/issues/304) – notably their CustomLogger and CustomConfigurationReader classes for the purposes of calling Agent.Setup(). This does work but seemingly contradicts the notion that the public API doesn’t require setup.

I tried the HttpListenerSample from the Github repo’s sample code – I literally copied and pasted the provided code into a new project’s Program.cs and ran it. This sample code worked, even with the Agent.Subscribe() call commented out – so only the public API was called - there was no configuration, so the defaults all worked.

I’m really puzzled and probably missing something simple, but for example the following code does not send entries to the APM server. I admit that I’m a little outside my comfort zone here and understand that I’m about to ask a pretty broad question, but can you provide any guidance as to what I’m missing, and futher – what is the best way to get at configurations via files e.g., appsettings.json or via environment variables?
using System;
using System.Threading;
using Elastic.Apm;
using Elastic.Apm.Api;

namespace apmapp2
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Thread.Sleep(150);
            Agent.Tracer.CaptureTransaction("ApmApp2.Transaction", ApiConstants.ActionExec, () =>
            {
                    Console.WriteLine("Hello World! " + (GetRandomNum()).ToString());
            });
            
        }

        private static int GetRandomNum() => Agent.Tracer.CurrentTransaction.CaptureSpan("ApmApp2.Transaction.GetRandomNum", "Random", () =>
        {
            Thread.Sleep(150);
            return new Random().Next();
        });
    }
}
And corresponsding appsettings.json:
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ElasticApm":
    {
      "ServiceName": "ApmTest3",
      "LogLevel":  "Debug",
      "ServerUrls":  "http://localhost:8200",
      "TransactionSampleRate": 1.0
    }
}

Welcome @MattK,

this probably happens, because the application terminates before it has the chance to send the data to the APM Server. Could you please add some sleep, eg. Thread.Sleep(1000) before the closing } in the Main method? The agent does not stop the app from terminating, even if there are events that are not yet sent.

Do you have short living processes, or is this just a sample app?

Also, I'd like to address this part:

notably their CustomLogger and CustomConfigurationReader classes for the purposes of calling Agent.Setup(). This does work but seemingly contradicts the notion that the public API doesn’t require setup.

Yeah, that is not really needed, and honestly I don't know why they did that in the referenced GitHub Issue. There is 1 open bug, which I think is not related to this, which is this one: https://github.com/elastic/apm-agent-dotnet/issues/180.

I hope the 1. part explains your issue, the the issue with the Agent.Setup was more like an fyi.

@GregKalapos Thanks for the prompt reply!

It looks like you are 100% correct - my test applications are terminating before the APM agent is done sending to the server. Adding additional sleep time or keeping the application open by any other means does indeed give the agent time to finish its work.

To answer your question - I'm working only with test applications and localhost environments at this point in time.

My senior developer and I would also like to know if more of the internal classes will be documented. My senior dev is particularly interested in the "components" used by the ApmComponents class. I can mostly follow what the agent is doing "under the hood," but I'm just an intern and we'd like to know what's going on "for sure."

I've learned a lot from working with the Elastic stack and .net APM agent. Thanks again!

Thanks for the reply - happy to see it helped.

I'm working only with test applications and localhost environments at this point in time.

Great, I see. Yeah, there are 2 known scenarios when this problem happens, 1 is very very small sample apps, and the other one is short running processes - like batch jobs and things like that.

The 1. one isn’t a real issue, it can be solved with not terminating the process immediately, the 2. one is more important, since in those cases we don’t want to change the app - for that scenario we plan to come up with some solution long term.

My senior developer and I would also like to know if more of the internal classes will be documented. My senior dev is particularly interested in the "components" used by the ApmComponents class

Well, we document the code on a level that makes it understandable for other developers. But given that the agent is 100% open source we don’t plan to publish a document about its details, since the code is open and everyone can debug through the agent.

Nevertheless I’m happy to answer any question.

Specifically the AgentComponents is just a class that holds parts of the agent which are necessary for the agent to work. For example it knows the Logger (which logs what the agent does), the IPayloadSender (which sends all the events - like spans, transactions, and metrics - to the APM Server), and also it has a ConfigurationReader that reads the agent configuration. The whole point of it is to replace components as easy as possible, for example reading configs from environment variables on .NET Framework is different from reading configs from the appsettings.json file on ASP.NET Core - but all these components are interchangeable through AgentComponents.

For capturing things automatically we mostly use DiagnosticSource - plus we have an ASP.NET Core middleware that captures all incoming requests.

In case you only use the public API then it’s event simpler, for all those Agent.Tracer.CaptureTransaction and CaptureSpan methods we just create a span or a transaction, we measure the time between the 1. and the last line of the Transaciton/Span and we send those to the server.

Let me know in case you have any question - and also feel free to simply debug through the agent code - nothing should be a secret there :slight_smile:

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.