# Use APM in a Windows Service (non .net core)

**URL:** <https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985>\
**Category:** APM\
**Tags:** dotnet\
**Created:** [April 1, 2020, 6:28am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985 "2020-04-01T06:28:30Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![frutty](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/frutty/32/65448_2.png) [@frutty](https://discuss.elastic.co/u/frutty)\
**Post date:** [April 1, 2020, 6:28am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/1 "2020-04-01T06:28:30Z")

</div>

Hey, I've been trying to find an answer to this in the forum but I'm not able to find anything relevant.

I want to be able to create transaction for different methods in our code.  
Filtering data/db transactions etc...

The application is an standard windows service application.

If I understand correctly I need to use the Agent Public API?  
I've tried to configure it but I can't seem to get it right.

Anyone here that has done this or is it impossible?  
We have several windows services in our team that we want to do this for.  
Some of them are TopShelf services if that makes any difference.

BR  
Frutty

---

<div class="post-metadata">

**Author:** ![GregKalapos](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/gregkalapos/32/37205_2.png) [@GregKalapos](https://discuss.elastic.co/u/GregKalapos)\
**Post date:** [April 1, 2020, 9:24am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/2 "2020-04-01T09:24:34Z")

</div>

Hi @frutty,

> If I understand correctly I need to use the Agent Public API?

Correct, [in this case](https://www.elastic.co/guide/en/apm/agent/dotnet/current/setup.html#setup-general) all you need to do is to reference the [Elastic.APM](https://www.nuget.org/packages/Elastic.Apm/) package and use the [Public Agent API](https://www.elastic.co/guide/en/apm/agent/dotnet/current/public-api.html) to create transactions in your code.

> Anyone here that has done this or is it impossible?

That should be possible - what you describe here is definitely a use-case that should work.

What is exactly the issue? Have you already added code to capture a transaction?

---

<div class="post-metadata">

**Author:** ![frutty](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/frutty/32/65448_2.png) [@frutty](https://discuss.elastic.co/u/frutty)\
**Post date:** [April 1, 2020, 10:25am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/3 "2020-04-01T10:25:41Z")

</div>

[GregKalapos](https://discuss.elastic.co/u/GregKalapos)  
Yes I've already added the code for the transactions.  
The issue is how should I configure the APM agent?  
For the application I'm working with now we have our APM settings in our app.config.

And it just hit me, is the Public Agent Api available for ECE?

---

<div class="post-metadata">

**Author:** ![GregKalapos](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/gregkalapos/32/37205_2.png) [@GregKalapos](https://discuss.elastic.co/u/GregKalapos)\
**Post date:** [April 1, 2020, 10:44am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/4 "2020-04-01T10:44:20Z")

</div>

> The issue is how should I configure the APM agent?

You can use environment variables. If you look at [the list of our configs](https://www.elastic.co/guide/en/apm/agent/dotnet/current/config-all-options-summary.html) then for every config setting you'll have a column with "Environment variable name" - e.g. to set the [APM Server url](https://www.elastic.co/guide/en/apm/agent/dotnet/current/config-reporter.html#config-server-urls) the environment variable name is `ELASTIC_APM_SERVER_URLS`. Just make sure that the process with the agent can read this environment variable.

> For the application I'm working with now we have our APM settings in our app.config.

Unfortunately if you only use the Public Agent API the agent won't read `app.config` - it'll only work with environment variables. You could extend the agent to read `app.config`, but that needs some coding. Wouldn't just using environment variables be enough?

> And it just hit me, is the Public Agent Api available for ECE?

Yes, you can basically use it anywhere - once you have an APM Server that sends the data to Elasticsearch, all parts of the agent should work. ECE should be also ok.

---

<div class="post-metadata">

**Author:** ![frutty](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/frutty/32/65448_2.png) [@frutty](https://discuss.elastic.co/u/frutty)\
**Post date:** [April 15, 2020, 6:18am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/5 "2020-04-15T06:18:22Z")

</div>

Thanks for the reply.

Is there an code example of this somewhere?

I will try to use the environment variables and see if I get that to work.

---

<div class="post-metadata">

**Author:** ![GregKalapos](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/gregkalapos/32/37205_2.png) [@GregKalapos](https://discuss.elastic.co/u/GregKalapos)\
**Post date:** [April 15, 2020, 10:32am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/6 "2020-04-15T10:32:10Z")

</div>

Hi @frutty

> Is there an code example of this somewhere?

Code example of what part?

If you refer to my comment on reading the configs from a source which is not environment variables then [this .cs file](https://github.com/elastic/apm-agent-dotnet/blob/master/src/Elastic.Apm.AspNetFullFramework/FullFrameworkConfigReader.cs) is a good place to look at.

If you generally need code samples on how to use the public agent API, I'd say [the official doc](https://www.elastic.co/guide/en/apm/agent/dotnet/current/public-api.html) is the best place.

We also have some small code snippets [here](https://github.com/elastic/apm-agent-dotnet/blob/master/sample/ApiSamples/Program.cs) and [here](https://github.com/elastic/apm-agent-dotnet/blob/master/sample/SampleAspNetCoreApp/Controllers/HomeController.cs).

---

<div class="post-metadata">

**Author:** ![frutty](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/frutty/32/65448_2.png) [@frutty](https://discuss.elastic.co/u/frutty)\
**Post date:** [April 17, 2020, 6:02am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/7 "2020-04-17T06:02:54Z")

</div>

@GregKalapos Thanks again for the response.

The first .cs file helps but isn't there any code example of how to set up the agent?

Maybe I'm stupid here but I'm not quite sure on which properties to set on which object.  
I just have 4 properties I want to assign my APM Agent:

- _ServerUrls_
- _ServiceName_
- _TransactionSampleRate_
- _SecretToken_

When I try to use the Elastic.Apm.Agent.Setup() method it needs a ApmLogger/IConfigurationReader/IPayLoadSender.

I can't find any documentation on how to use this method. Which I assume is the one that I should use in order to configure the APM Agent?

BR  
Frutty

EDIT: It just hit me, is the intented way to implement each of the interfaces?....

EDIT 2: And to use it like this?

```
public class ApmConfigurationReader : IConfigurationReader
{
    public ApmConfigurationReader()
    {            
        ServiceName = ConfigurationManager.AppSettings["elasticapm.ServiceName"].Trim();
        ServerUrls = new List<Uri> { new Uri(ConfigurationManager.AppSettings["elasticapm.ServerUrls"].Trim()) };
        TransactionSampleRate = double.Parse(ConfigurationManager.AppSettings["elasticapm.TransactionSampleRate"].Trim());
        SecretToken = ConfigurationManager.AppSettings["elasticapm.SecretToken"].Trim();
    }

    public string CaptureBody { get; }
    public List<string> CaptureBodyContentTypes { get; }
    public bool CaptureHeaders { get; }
    public bool CentralConfig { get; }
    public IReadOnlyList<WildcardMatcher> DisableMetrics { get; }
    public string Environment { get; }
    public TimeSpan FlushInterval { get; }
    public IReadOnlyDictionary<string, string> GlobalLabels { get; }
    public LogLevel LogLevel { get; }
    public int MaxBatchEventCount { get; }
    public int MaxQueueEventCount { get; }
    public double MetricsIntervalInMilliseconds { get; }
    public IReadOnlyList<WildcardMatcher> SanitizeFieldNames { get; }
    public string SecretToken { get; }
    public string ApiKey { get; }
    public IReadOnlyList<Uri> ServerUrls { get; }
    public string ServiceName { get; }
    public string ServiceNodeName { get; }
    public string ServiceVersion { get; }
    public double SpanFramesMinDurationInMilliseconds { get; }
    public int StackTraceLimit { get; }
    public int TransactionMaxSpans { get; }
    public double TransactionSampleRate { get; }
    public bool UseElasticTraceparentHeader { get; }
    public bool VerifyServerCert { get; }
}

//In program.cs
Agent.Setup(new AgentComponents(null, new ApmConfigurationReader(), null));

```

EDIT 3: Just tried this.  
The Apm.Agent.Setup() relies on assembly System.Net.Http version 4.2.0.0 and I've tried to install several versions of the System.Net.Http nuget but with zero success.

---

<div class="post-metadata">

**Author:** ![GregKalapos](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/gregkalapos/32/37205_2.png) [@GregKalapos](https://discuss.elastic.co/u/GregKalapos)\
**Post date:** [April 17, 2020, 3:22pm UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/8 "2020-04-17T15:22:34Z")

</div>

@frutty you can customize the agent by passing custom components to it with `Apm.Agent.Setup()`, but that's optional.

If you don't call `Setup()`, the agent will read configs from environment variable. For all configs you pointed out in your question (ServerUrls, ServiceName, etc.), you can find the corresponding environment variable in [this list](https://www.elastic.co/guide/en/apm/agent/dotnet/current/config-all-options-summary.html) (just click on each and it'll lead you to a page with the info about the setting including the environment variable names).

So unless there is a good reason to pass custom components to the agent (like a custom config reader) I'd not bother with that. Also, it's ok to just set the environment variable in C# when your service starts - this is an easy workaround I see people sometimes do.

---

<div class="post-metadata">

**Author:** ![sachinp200](https://avatars.discourse-cdn.com/v4/letter/s/e19adc/32.png) [@sachinp200](https://discuss.elastic.co/u/sachinp200)\
**Post date:** [May 5, 2020, 9:07am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/9 "2020-05-05T09:07:26Z")

</div>

I am also struggling with the same issue for how to setup Agent for a TopShelf .NET Core Windows service using EF Core. Do I need to use Elastic.Apm.NetCoreAll or Elastic.Apm package? Of which Object shall I set the Environment Variable? You have given good example of doing it in WEB API however it is missing for Windows Service.  
Need to know what is the use of this method in your Public API sample - PassDistributedTracingData. Why it is only setting Service Name? Do I need to set others like Server Url, etc. in this function. How can I use this function and I put the transaction on my start of the process however, don't know how and where to setup the Agent. Please provide some working code sample.  
public override void Work()  
{  
var transaction = Agent.Tracer.StartTransaction("DataUploadServiceTransaction", "DataUploadService");  
try  
{  
Process();  
}  
catch(Exception ex)  
{  
transaction.CaptureException(ex);  
}  
finally  
{  
transaction.End();  
}  
}

---

<div class="post-metadata">

**Author:** ![GregKalapos](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/gregkalapos/32/37205_2.png) [@GregKalapos](https://discuss.elastic.co/u/GregKalapos)\
**Post date:** [May 5, 2020, 9:32am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/10 "2020-05-05T09:32:56Z")

</div>

Hi @sachinp200,

Please take a look at [our documentation](https://www.elastic.co/guide/en/apm/agent/dotnet/1.x/index.html) - all this should be covered there.

Let me help a bit with finding the right parts for your question:

> Do I need to use Elastic.Apm.NetCoreAll or Elastic.Apm package?

This is covered [here](https://www.elastic.co/guide/en/apm/agent/dotnet/1.x/setup.html#setup-asp-net-core) - below the code sample.

> Of which Object shall I set the Environment Variable?

[This part](https://www.elastic.co/guide/en/apm/agent/dotnet/master/public-api.html#api-initialization) explains this.

> You have given good example of doing it in WEB API however it is missing for Windows Service.

That should not change this - config reading is the same on both.

> Need to know what is the use of this method in your Public API sample - PassDistributedTracingData

The `DistributedTracingData` parameter is documented [here](https://www.elastic.co/guide/en/apm/agent/dotnet/master/public-api.html#convenient-capture-transaction)

> Why it is only setting Service Name?

I assume you mean [this line](https://github.com/elastic/apm-agent-dotnet/blob/master/sample/ApiSamples/Program.cs#L161) - as some of the doc above describes you can start the agent without setting any setting, in those case the agent starts with default values - it sends data to an APM server on localhost and sets the service name to the name of the entry assembly. The Public API sample is a bit special in this regard, because it starts the same assembly (executable in this case) twice to showcase distributed tracing - but given it's the same assembly it'd have the same service name. In order to avoid this, we change the service name from default to a specific one before we start the app the 2. time, this way it'll be 2 different services which is needed to better showcase distributed tracing.

> Do I need to set others like Server Url, etc. in this function. How can I use this function and I put the transaction on my start of the process however, don't know how and where to setup the Agent.

I think all those were covered in above links - if not let me know.

---

<div class="post-metadata">

**Author:** ![sachinp200](https://avatars.discourse-cdn.com/v4/letter/s/e19adc/32.png) [@sachinp200](https://discuss.elastic.co/u/sachinp200)\
**Post date:** [May 5, 2020, 10:02am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/11 "2020-05-05T10:02:05Z")

</div>

Thanks Greg, I have gone through those links already. I have tried setting up environment variables in the Windows and also through the following code however I am not getting anything logged on Kibana APM module. I can successfully log for Web APIs. I am pasting my code below, let me know if this is not the way to do it and what is missing:  
public override void Work()  
{  
Agent.Subscribe(new EfCoreDiagnosticsSubscriber());  
var startInfo = new ProcessStartInfo();  
startInfo.Environment["ELASTIC\_APM\_SERVICE\_NAME"] = "DataUploadService";  
startInfo.Environment["ELASTIC\_APM\_SERVER\_URLS"] = "[http://ec2-42-85-137-237.ap-southeast-2.compute.amazonaws.com:8200](http://ec2-42-85-137-237.ap-southeast-2.compute.amazonaws.com:8200)";  
var outgoingDistributedTracingData = (Agent.Tracer.CurrentSpan?.OutgoingDistributedTracingData  
?? Agent.Tracer.CurrentTransaction?.OutgoingDistributedTracingData)?.SerializeToString();  
var transaction = Agent.Tracer.StartTransaction("DataUploadServiceTransaction", ApiConstants.ActionExec,  
DistributedTracingData.TryDeserializeFromString(outgoingDistributedTracingData));  
System.Diagnostics.Process.Start(startInfo);  
try  
{  
MyProcess();  
}  
catch(Exception ex)  
{  
transaction.CaptureException(ex);  
}  
finally  
{  
transaction.End();  
}  
}

---

<div class="post-metadata">

**Author:** ![GregKalapos](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/gregkalapos/32/37205_2.png) [@GregKalapos](https://discuss.elastic.co/u/GregKalapos)\
**Post date:** [May 5, 2020, 10:18am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/12 "2020-05-05T10:18:45Z")

</div>

Do you really need to start a new process here?

I think the issue is this part:

```auto
var startInfo = new ProcessStartInfo();
startInfo.Environment["ELASTIC_APM_SERVICE_NAME"] = "DataUploadService";
startInfo.Environment["ELASTIC_APM_SERVER_URLS"] = "http://ec2-42-85-137-237.ap-southeast-2.compute.amazonaws.com:8200";

```

In the sample what you see we spawn a new process - that is what we use `ProcessStartInfo` for, but in your case probably you don't need that.

With `ProcessStartInfo` you don't set environment variables for your current process.

Instead, do this:

```auto
Environment.SetEnvironmentVariable("ELASTIC_APM_SERVICE_NAME", "DataUploadService");
Environment.SetEnvironmentVariable("ELASTIC_APM_SERVER_URLS", "http://ec2-42-85-137-237.ap-southeast-2.compute.amazonaws.com:8200";);

```

Also, just to be on the safe side, I'd suggest moving these lines before any other agent code.

---

<div class="post-metadata">

**Author:** ![sachinp200](https://avatars.discourse-cdn.com/v4/letter/s/e19adc/32.png) [@sachinp200](https://discuss.elastic.co/u/sachinp200)\
**Post date:** [May 5, 2020, 10:29am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/13 "2020-05-05T10:29:36Z")

</div>

This time my code looks like this:  
using Elastic.Apm;  
using Elastic.Apm.Api;

public class DataJob : Job  
{  
private readonly IDataService \_DataService;  
public DataJob(JobSetting setting, ILogger logger, IDataService DataService)  
: base(setting, logger)  
{  
\_DataService = DataService;  
}

```
    public override void Work()
    {
        Environment.SetEnvironmentVariable("ELASTIC_APM_SERVICE_NAME", "DataUploadService");
        Environment.SetEnvironmentVariable("ELASTIC_APM_SERVER_URLS", "http://ec2-42-85-137-237.ap-southeast-2.compute.amazonaws.com:8200");
        Agent.Subscribe(new EfCoreDiagnosticsSubscriber());
       
        var outgoingDistributedTracingData = (Agent.Tracer.CurrentSpan?.OutgoingDistributedTracingData
            ?? Agent.Tracer.CurrentTransaction?.OutgoingDistributedTracingData)?.SerializeToString();            
        var transaction = Agent.Tracer.StartTransaction("DataUploadServiceTransaction", ApiConstants.ActionExec,
                DistributedTracingData.TryDeserializeFromString(outgoingDistributedTracingData));
       
        try
        {
            Process();
        }
        catch(Exception ex)
        {
            transaction.CaptureException(ex);
        }
        finally
        {
            transaction.End();
        }            
    }

    private void Process()
    {
        LoadExcelDataToStagingTable();                      
    }

```

}

But still nothing logged on the Kibana APM module.

---

<div class="post-metadata">

**Author:** ![GregKalapos](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/gregkalapos/32/37205_2.png) [@GregKalapos](https://discuss.elastic.co/u/GregKalapos)\
**Post date:** [May 5, 2020, 10:45am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/14 "2020-05-05T10:45:24Z")

</div>

@sachinp200 That APM Server seems to run in Elastic Cloud, is that right? In that case you'll also set the [SecretToken](https://www.elastic.co/guide/en/apm/agent/dotnet/current/config-reporter.html#config-secret-token). Can that be the missing part?

---

<div class="post-metadata">

**Author:** ![sachinp200](https://avatars.discourse-cdn.com/v4/letter/s/e19adc/32.png) [@sachinp200](https://discuss.elastic.co/u/sachinp200)\
**Post date:** [May 5, 2020, 10:54am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/15 "2020-05-05T10:54:16Z")

</div>

Yes Greg, it is running in cloud and I didn't use secret token for Web APIs and they are loggin data. BTW, can also use your help in knowing how to generate the token.

---

<div class="post-metadata">

**Author:** ![GregKalapos](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/gregkalapos/32/37205_2.png) [@GregKalapos](https://discuss.elastic.co/u/GregKalapos)\
**Post date:** [May 5, 2020, 12:00pm UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/16 "2020-05-05T12:00:43Z")

</div>

> BTW, can also use your help in knowing how to generate the token.

If you go into your deployment and click to "APM" you can see the "  
APM Server secret token" - there is also a reset button for it.

> I didn't use secret token for Web APIs and they are loggin data.

Hmm... that's strange. Unless you changed something I think it should not work, but I'm not totally sure what's happening there.

I'd suggest looking into your APM info on cloud and try it with the secret token - I suspect it'd solve this, if not, we can look into other options.

---

<div class="post-metadata">

**Author:** ![sachinp200](https://avatars.discourse-cdn.com/v4/letter/s/e19adc/32.png) [@sachinp200](https://discuss.elastic.co/u/sachinp200)\
**Post date:** [May 6, 2020, 12:41am UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/17 "2020-05-06T00:41:34Z")

</div>

Hi Greg, I will try the secret token part and get back to you if it doesn't work. However, I got it working now without secret token. Thanks for your help.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [May 26, 2020, 8:41pm UTC](https://discuss.elastic.co/t/use-apm-in-a-windows-service-non-net-core/225985/18 "2020-05-26T20:41:43Z")

</div>

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