Track Custom ActivitySource with Application Insights - c#

I am trying to get application insights to pick up on custom ActivitySource in a library, however the documentation is unclear on how to achieve this.
Currently I have the following:
...
public static readonly ActivitySource Source = new ActivitySource("MyCompany.Library");
...
In the library it is used like this:
using(var activity = Source.StartActivity("Action"))
{
...
}
And in my startup I've added the following:
services.ConfigureTelemetryModule<DependencyTrackingTelemetryModule>(
(m, o) => m.IncludeDiagnosticSourceActivities.Add("MyCompany.Library")
);
services.AddApplicationInsightsTelemetryWorkerService();
However, these activities are not being picked up by application insights.
Is there something else required to make application insights aware of these activities ?
I'd rather not 'pollute' these libraries with application insights code

ApplicationInsights SDKs does not support reporting telemetry from custom ActivitySource.
There is a preview version which supports ActivitySource based telemetry. (Its called OpenTelemetry AzureMonitorExporter)
https://learn.microsoft.com/azure/azure-monitor/app/opentelemetry-enable?tabs=net

Follow the below steps to operate:
Add the OpenTelemetry.Exporter.Console NuGet package.
dotnet add package OpenTelemetry.Exporter.Console
Update Program.cs with additional OpenTelemetry using directives
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
Update Main() to create the OpenTelemetry TracerProvider:
public static async Task Main()
{
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MySample"))
.AddSource("Sample.DistributedTracing")
.AddConsoleExporter()
.Build();
await DoSomeWork();
Console.WriteLine("Example work done");
}
Now the app collects distributed trace information and displays it to the console:
> dotnet run
You will get the result required.
Follow the below link for further reference:
https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-collection-walkthroughs

Related

Azure Function c# - Retrieve list of subscription's WebApp

We want to create an azure function in c# that retrieve the list of azure web app contained in the subscription (basically we want to call dynamically, for each webapp, the same API endpoint changing the subdomain of the api).
It's possible with c# retrieve the list of the web app contained in the same azure function subscriptions?
Usually we connect to the master database, we query the sys.databases to collect the dbname and understand the webapp names. But we are searching for a smartest way.
If you're in C# land, I'd look at using the ArmClient class to retrieve what you're looking for.
Install these (I've got a few others installed but start with that and see how you go, there may be a couple of others needed) Nuget packages ...
Azure.Identity;
Azure.ResourceManager;
Azure.ResourceManager.AppService
... and from there, using the DefaultCredential approach (if you've never used it, read up on it here -> https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/README.md) you can query your subscriptions webApps ...
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.AppService;
using System;
using System.Threading.Tasks;
namespace AzureManagement
{
internal class Program
{
static void Main(string[] args)
{
GetAzureResources().Wait();
}
static async Task GetAzureResources()
{
var credential = new DefaultAzureCredential();
var armClient = new ArmClient(credential);
var subscription = await armClient.GetDefaultSubscriptionAsync();
var webSitesEnumerator = subscription.GetWebSitesAsync().GetAsyncEnumerator();
try
{
while (await webSitesEnumerator.MoveNextAsync())
{
var webSite = webSitesEnumerator.Current;
Console.WriteLine($"Web App Name ........ {webSite.Data.Name}");
Console.WriteLine($"Default Host Name ... {webSite.Data.DefaultHostName}\n");
}
}
finally
{
await webSitesEnumerator.DisposeAsync();
}
Console.ReadLine();
}
}
}
The above is obviously not a function app but the core code will still work for you and can be ported as need be.
Note: I could be telling you how to suck eggs, but, once deployed to Azure, you'll need to do the necessary work to ensure that the function app has the required access to retrieve all of the resource information you're looking for.
If you're unfamiliar with that, read up on the managed identity concept. It's very easy to setup -> https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity
Yes, one easy way is to use HttpClient and send a request to Azure Rest API:
GET https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Web/sites?api-version=2022-03-01
https://learn.microsoft.com/en-us/rest/api/appservice/web-apps/list
PS: you first need to acquire an authentication token.
https://www.youtube.com/watch?v=6b1J03fDnOg&t=329s

The "correct" way to create a .NET Core console app without background services

I'm building a simple .NET Core console application that will read in basic options from the command line, then execute and terminate without user interaction. I'd like to take advantage of DI, so that lead me to using the .NET Core generic host.
All of the examples I've found that build a console app create a class that either implements IHostedService or extends BackgroundService. That class then gets added to the service container via AddHostedService and starts the application's work via StartAsync or ExecuteAsync. However, it seems that in all of these examples, they are implemementing a background service or some other application that runs in a loop or waits for requests until it gets shut down by the OS or receives some request to terminate. What if I just want an app that starts, does its thing, then exits? For example:
Program.cs:
namespace MyApp
{
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public static class Program
{
public static async Task Main(string[] args)
{
await CreateHostBuilder(args).RunConsoleAsync();
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseConsoleLifetime()
.ConfigureLogging(builder => builder.SetMinimumLevel(LogLevel.Warning))
.ConfigureServices((hostContext, services) =>
{
services.Configure<MyServiceOptions>(hostContext.Configuration);
services.AddHostedService<MyService>();
services.AddSingleton(Console.Out);
});
}
}
MyServiceOptions.cs:
namespace MyApp
{
public class MyServiceOptions
{
public int OpCode { get; set; }
public int Operand { get; set; }
}
}
MyService.cs:
namespace MyApp
{
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
public class MyService : IHostedService
{
private readonly MyServiceOptions _options;
private readonly TextWriter _outputWriter;
public MyService(TextWriter outputWriter, IOptions<MyServiceOptions> options)
{
_options = options.Value;
_outputWriter = outputWriter;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
_outputWriter.WriteLine("Starting work");
DoOperation(_options.OpCode, _options.Operand);
_outputWriter.WriteLine("Work complete");
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_outputWriter.WriteLine("StopAsync");
}
protected void DoOperation(int opCode, int operand)
{
_outputWriter.WriteLine("Doing {0} to {1}...", opCode, operand);
// Do work that might take awhile
}
}
}
This code compiles and runs just fine, producing the following output:
Starting work
Doing 1 to 2...
Work complete
However, after that, the application will just sit there waiting until I press Ctrl+C. I know I could force the application to shutdown after the work is complete, but at this point, I feel like I'm not using IHostedService correctly. It seems as though it's designed for recurring background processes, and not simple console applications like this. However, in an actual application where DoOperation might take 20-30 minutes, I would like to take advantage of the StopAsync method to do cleanup before terminating. I also know I could create the service container myself and all that, but the .NET Core generic host already does a lot of stuff I would want to do anyway. It seems to be the right way to write console applications, but without adding a hosted service that kicks off the actual work, how do I get the app to actually do anything?
Instead of a hosted service, I would recommend the following;
using (var host = CreateHostBuilder(args).Build())
{
await host.StartAsync();
var lifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
// do work here / get your work service ...
lifetime.StopApplication();
await host.WaitForShutdownAsync();
}
I know I could force the application to shutdown after the work is complete, but at this point, I feel like I'm not using IHostedService correctly.
I agree it does seem odd. I actually always stop the application at the end of all my IHostedService implementations. This is true even for long-running server apps. If a hosted service stops (or faults), then I explicitly want the application to end.
It does feel like this design was unfinished when .NET Core was pushed out. There are parts of the design that are made so that hosted services and their apps can have independent lifetimes, but since they can not be restarted, this just isn't useful in practice. So it ends up feeling like a poor design because the lifetimes can't be independent, but they are independent by default.
All of my hosted services end up tying their lifetime to the application lifetime.
finally
{
_hostApplicationLifetime.StopApplication();
}

Using LoggerFactory to output to console; Issue with .AddConsole()

I am attempting to learn about and implement logging in a c# winforms application.
In most/all examples and documentation I have found they use .AddConsole() which provides me with the following error:
'ILoggingBuilder' does not contain a definition for AddConsole and no accessible extention method 'Add Console'...
My current code follows:
using Microsoft.Extensions.DependencyInjection; //<-----was suggested in an example but does nothing--<<<<
using Microsoft.Extensions.Logging;
public partial class FormMain : Form
{
public FormMain()
{
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
.AddConsole() //<-----------------------line with error---------<<<<
.AddEventLog();
});
ILogger logger = loggerFactory.CreateLogger<FormMain>();
logger.LogInformation("Example log message");
InitializeComponent();
...rest of program follows...
Can you assist in pointing out how to get the example code to compile?
If you have a link to where a tutorial/documentation that I can follow, I would greatly appreciate that as well.
I see in the Microsoft Docs that there is an ILoggerFactory.AddProvider(ILoggerProvider) method but I have not had any success with that either.
Thank you for your time.
There are various different logging mechanisms and you don't get them out of the box. You are missing the appropriate nuget package. You need to add a package reference to Microsoft.Extensions.Logging.Console. Once you do that, the extension methods should be available to you.
You can find the package on nuget.org.

How can I pass a SSL certificate to Nowin when using Nancy

So I am using Nancy with Nowin.
The beauty of using Nowin is I don't have to mess around with various Windows commands to set up a simple web server. According to the Nowin readme I can configure SSL using the following line
builder.SetCertificate(new X509Certificate2("certificate.pfx", "password"));
However, when using Nancy I don't seem to have access to this Server builder class. Everything seems to happen magically behind the scenes.
Any ideas how I can pass the certificate through to Nowin?
Make sure you have the Nancy.Owin package installed.
Use code like this to start the server up:
.
using System;
using System.Net;
using System.Threading.Tasks;
using Nancy.Owin;
using Nowin;
public class Program
{
static void Main(string[] args)
{
var myNancyAppFunc = NancyMiddleware.UseNancy()(NancyOptions options =>
{
// Modify Nancy options if desired;
return Task.FromResult(0);
});
using (var server = ServerBuilder.New()
.SetOwinApp(myNancyAppFunc)
.SetEndPoint(new IPEndPoint(IPAddress.Any, 8080))
.SetCertificate(new X509Certificate2("certificate.pfx", "password"))
.Build()
)
{
server.Start();
Console.WriteLine("Running on 8080");
Console.ReadLine();
}
}
}
If you look at this document, it says the following:
Configuration of OWIN
It'll just be there if the host sends it on.
If you use IIS as a host. You'll need to do the same config as with Aspnet. And you'll need an OWIN Aspnet host that supports the ClientCertificate. The one in the OWIN demo in Nancy does. The one by #prabirshrestha also does.
In the OWIN Demo, check this line:
if (request.ClientCertificate != null && request.ClientCertificate.Certificate.Length != 0)
{
env[OwinConstants.ClientCertificate] = new X509Certificate(request.ClientCertificate.Certificate);
}
Hope it helps you, good luck.
I think you should follow by the way described in this article: https://msdn.microsoft.com/en-us/magazine/dn451439.aspx
At first you are creating web server according with Nowin documentation and after that you are adding Nancy as pipeline component. I tested this way with NowingSample (from Nowin package) and it works.

Azure WebJobs QueueTrigger not triggering

I'm trying to find what I'm doing wrong regarding an Azure WebJobs QueueTrigger method that should be triggered from an Azure Storage Queue.
I've read a couple of documents (as in blog posts / msdn articles). But I'm still not clear.
Main question / misunderstood aspect:
What should be the name of the connection string for Azure storage console app App.config or Windows Azure Configuration (portal). So far I have the following name set at both places.
AzureJobsStorage
AzureWebJobsStorage
AzureJobsRuntime
AzureJobsDashboard
AzureJobsData
Here's my WebJobs console app code.
static void Main()
{
JobHost host = new JobHost();
host.RunAndBlock();
}
public static void CreateLeague([QueueTrigger("temp")] string msg)
{
var task = JsonConvert.DeserializeObject<QueueTask>(msg);
if (task.TaskType == QueueTask.TaskTypes.Pdf)
RenderPdf(task.Id);
}
This console app is continuously running on my Azure Website.
I can access its "debug" page where I can toggle output and I see it is started / running.
My code to add queue (from my ASP.NET MVC app):
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["StorageConnectionString"]);
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference("temp");
queue.CreateIfNotExists();
Common.QueueTask task = new Common.QueueTask();
task.TaskType = Common.QueueTask.TaskTypes.Pdf;
task.Id = p.Id;
CloudQueueMessage msg = new CloudQueueMessage(JsonConvert.SerializeObject(task) );
queue.AddMessage(msg);
This code is executed, and queue are added to my Storage Account. But they did not get "dequeue" or read from the WebJobs.
Hmm, the WebJobs class had to be public.
using Microsoft.Azure.WebJobs;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Newtonsoft.Json;
using Proceed.Common;
using System;
using System.Configuration;
using System.IO;
public class WebJobsTask {
public static void Main()
{
JobHost host = new JobHost();
host.RunAndBlock();
}
public static void CreateLeague([QueueTrigger("temp")] string msg)
{
var task = JsonConvert.DeserializeObject<QueueTask>(msg);
if (task.TaskType == QueueTask.TaskTypes.Pdf)
RenderPdf(task.Id);
}
}
Also found a quick way to explore my queues: https://azurestorageexplorer.codeplex.com/.
In my case, I had assumed that QueueTrigger was referring to Service Bus Queues instead of Azure Queues, and I actually needed to use ServiceBusTrigger.
You can use the server explorer in VS to explore the content of the Storage queues.
The queue triggers for the WebJobs SDK will exponentially back off if there is no work to do. There might be a delay between the moment a message is put in a queue and the moment when it is picked up. You can configure the maximum back off through the JobHostConfiguration.Queues.MaxPollingInterval property.
For the latest SDK you need two storage connection strings AzureWebJobsStorage and AzureWebJobsDashboard
This is a great place for more resources: https://learn.microsoft.com/en-us/azure/app-service-web/websites-webjobs-resources

Categories