Tracking hangfire background jobs with app insights - asp.net-core

I have set up app insights in Asp.net core application. All my web api requests are tracked on app insights and if I have any failures I can simply find them in Failures section.
However, I have also Hangfire background jobs running and if they are failing I can't find them on app insights. Also I have alert rule Whenever the total http server errors is greater than or equal to 1 count and I am not sure if hangfire 5xx errors will go under this condition.
So is there any way to track Hangfire jobs failures and get notified about them?

Hangfire handles most exceptions under the hood, so App Insights is not going to pick them up by default. There is also a bunch of configuration you have to do with App Insights as well.
I wrote a JobFilter for Hangfire which allows you to connect with App Insights, this should be enough to get you going:
https://github.com/maitlandmarshall/MIFCore/blob/master/MIFCore.Hangfire/Analytics/AppInsightsEventsFilter.cs
And for the App Insights configuration:
https://github.com/maitlandmarshall/MIFCore/blob/master/MIFCore.Hangfire/Analytics/TelemetryConfigurationFactory.cs
To put everything together from the above links:
var appInsights = this.rootScope.ResolveOptional<AppInsightsConfig>();
var childScope = ServiceScope = this.rootScope.BeginLifetimeScope("HangfireServiceScope");
var activator = new AutofacLifecycleJobActivator(childScope);
var options = new BackgroundJobServerOptions()
{
Activator = activator,
Queues = new[] { JobQueue.Alpha, JobQueue.Beta, JobQueue.Default, JobQueue.Low }
};
this.globalConfig
.UseFilter(new BackgroundJobContext());
if (!string.IsNullOrEmpty(appInsights?.InstrumentationKey))
{
var telemetryClient = new TelemetryClient(TelemetryConfigurationFactory.Create(appInsights));
this.globalConfig.UseFilter(new AppInsightsEventsFilter(telemetryClient));
}
using (var server = new BackgroundJobServer(options))
{
await server.WaitForShutdownAsync(stoppingToken);
}

There was a nice nuget package created Hangfire.Extensions.ApplicationInsights.
So, install the package:
Install-Package Hangfire.Extensions.ApplicationInsights
and add the line to ConfigureService method:
services.AddHangfireApplicationInsights();
If your solution requires some custom details you can adjust the code from github repository.

Related

Hangfire.Console logging not displayed in Dashboard

I've been using Hangfire for ages, but only recently discovered you can use Hangfire.Console to output logging, which can be seen in the Hangfire Dashboard.
I've seen it work on someone else's project, but when adding logging to my own project, I cannot make it work.
This is the gist of the task.
I checked with Debugger that all lines are executed and the job does finish successfully.
public async Task MethodName(PerformContext context)
{
var result = await apiClient.MethodName();
if (result != null)
{
context.SetTextColor(ConsoleTextColor.Green);
context.WriteLine($"Checked: {result.Checked}");
context.WriteLine($"Cleared: {result.Cleared}");
}
else
{
context.SetTextColor(ConsoleTextColor.Red);
context.WriteLine($"No result ...");
}
}
I expected to find the output in my Hangfire dashboard, but it shows no logging whatsoever. Is there a crucial step I am missing?
Using Hangfire 1.7.18 and Hangfire.Console 1.4.2

Task Module call from Ms Teams in Bot Framework

I am looking to open a task module (Pop up - iframe with audio/video) in my bot that is connected to Teams channel. I am having issues following the sample code provided on the GitHub page.
I have tried to follow the sample and incorporate to my code by did not succeed.
In my bot.cs file I am creating card action of invoke type:
card.Buttons.Add(new CardAction("invoke", TaskModuleUIConstants.YouTube.ButtonTitle, null,null,null,
new Teams.Samples.TaskModule.Web.Models.BotFrameworkCardValue<string>()
{
Data = TaskModuleUIConstants.YouTube.Id
}));
In my BotController.cs that inherits from Controller
[HttpPost]
public async Task PostAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await _adapter.ProcessAsync(Request, Response, _bot);
}
public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
if (activity.Type == ActivityTypes.Invoke)
{
return HandleInvokeMessages(activity);
}
return new HttpResponseMessage(HttpStatusCode.Accepted);
}
private HttpResponseMessage HandleInvokeMessages (Activity activity)
{
var activityValue = activity.Value.ToString();
if (activity.Name == "task/fetch")
{
var action = Newtonsoft.Json.JsonConvert.DeserializeObject<Teams.Samples.TaskModule.Web.Models.BotFrameworkCardValue<string>>(activityValue);
Teams.Samples.TaskModule.Web.Models.TaskInfo taskInfo = GetTaskInfo(action.Data);
Teams.Samples.TaskModule.Web.Models.TaskEnvelope taskEnvelope = new Teams.Samples.TaskModule.Web.Models.TaskEnvelope
{
Task = new Teams.Samples.TaskModule.Web.Models.Task()
{
Type = Teams.Samples.TaskModule.Web.Models.TaskType.Continue,
TaskInfo = taskInfo
}
};
return msg;
}
return new HttpResponseMessage(HttpStatusCode.Accepted);
}
There is more code as per the GitHub sample but I won't paste it here. Can someone point me into the correct direction ?
I have got to the stage that it is displaying a pop up window but the content and title comes from manifest file instead of creating actual iframe also no video is rendering. My goal is to render video within my teams using iframe container.
The important part from the sample:
This sample is deployed on Microsoft Azure and you can try it yourself by uploading Task Module CSharp.zip to one of your teams and/or as a personal app. (Sideloading must be enabled for your tenant; see step 6 here.) The app is running on the free Azure tier, so it may take a while to load if you haven't used it recently and it goes back to sleep quickly if it's not being used, but once it's loaded it's pretty snappy.
So,
Your Teams Admin MUST enable sideloading
Your bot MUST be sideloaded into Teams
The easiest way to do this would be download the sample manifest, open it in App Studio, then edit your bot information in. You then need to make sure Domains and permissions > Valid Domains are set for your bot. Also ensure you change the Tabs URLs to your own.
You also need to make sure that in your Tasks, the URLs they call ALL use https and not http. If anywhere in the chain is using http (like if you're using ngrok and http://localhost), it won't work.

Azure AD login inifinite loop

My code is entering an infinite loop, hitting azure login page (hosted by Microsoft), then redirecting back to my app, then back to ms host login page etc etc etc.
In my code I have a breakpoint in the OnAuthorizationCodeReceived event...
public void ConfigureAzureAd(IServiceCollection services)
{
//set authentication to use Azure AD
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
auth.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("OpenIdConnect").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async ctx =>
{
HttpRequest request = ctx.HttpContext.Request;
//We need to also specify the redirect URL used
string currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
//Credentials for app itself
var credential = new ClientCredential(ctx.Options.ClientId, ctx.Options.ClientSecret);
//Construct token cache
ITokenCacheFactory cacheFactory = ctx.HttpContext.RequestServices.GetRequiredService<ITokenCacheFactory>();
TokenCache cache = cacheFactory.CreateForUser(ctx.Principal);
var authContext = new AuthenticationContext(ctx.Options.Authority, cache);
//Get token for Microsoft Graph API using the authorization code
string resource = "https://graph.microsoft.com";
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
ctx.ProtocolMessage.Code, new Uri(currentUri), credential, resource);
//Tell the OIDC middleware we got the tokens, it doesn't need to do anything
ctx.HandleCodeRedemption(result.AccessToken, result.IdToken);
//ctx.HandleCodeRedemption();
}
};
});
}
and I can inspect the data in result, and it all looks ok (though not sure what failure would look like), it appears the login is working but my app is unable to recognize that the login has happened, or it's not saving, and keeps retrying
I've also asked someone else to try logging in with a user not in my Active Directory, and it fails appropriately, it really looks like Active Directory is happy, but my app just keeps redirecting.
I'm using .Net Core 2.2 (my first core project)
I'm using Active Directory Free
Update in response to #Marilee Turscak - MSFT
If i do not have the correct Reply Url setup in portal.azure.com and pass in it via C# then azure throws an error, so I've definitely got a reply URL in there and it matches correctly
Config looks like this:
"OpenIdConnect": {
"ClientId": "<guid in here>", // Application ID
"ClientSecret": "<secrect from portal.azure.com>",
"Authority": "https://login.microsoftonline.com/emailwithout#symbol.onmicrosoft.com/",
"PostLogoutRedirectUri": "http://www.<projectname_in_here>.local",
"CallbackPath": "/signin-oidc",
"ResponseType": "code id_token"
}
You need to set a Reply URL both in your code and in your application registration in Azure AD. You should set your Reply URL to wherever you want the user to be redirected (generally your main published homepage url - like https://myapp.azurewebsites.net).
For reference, you can see the examples in the Github samples.
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapp-openidconnect/
Answering my own question...
I think my issue was somehow related to feature folders. I was implementing custom routing to enable feature folders, but the Azure AD code sets it's own custom route to "/signin-oidc". I came to this conclusion by using Visual Studio to create a new project with Azure Active Directory wizard, got the test project signing-in, but when I ported my old code to the new test project, I got exactly the same error, but in the new "Visual Studio Wizard" there was very little configuration, AND it interfaced with my Azure AD and registered the app and added all the required configuration, so i knew it would before adding the feature folders, and produced exactly the same error behavior after feature folders, so conclude it was something to do with the feature folders custom routing.
Url to the code I found to help implement feature folders if anyone is interested: https://github.com/ardalis/OrganizingAspNetCore/tree/master/CoreFeatureFolders

UniqueConstraints bundle not picked up by EmbeddableDocumentStore with custom plugins directory

We are using EmbeddableDocumentStore for non-production deployments and in general it works great. I stumbled upon an issue which took me few hours to solve and it would be good to know if the behaviour I am experiencing is by design.
I init EmbeddableDocumentStore like this:
var store = new EmbeddableDocumentStore()
{
DataDirectory = dataDirectory,
DefaultDatabase = "DbName",
RunInMemory = false,
UseEmbeddedHttpServer = true,
};
store.Configuration.Port = 10001;
store.Configuration.PluginsDirectory = pluginsDirectory; // this is the important line
store.Configuration.CompiledIndexCacheDirectory = compiledIntexCacheDirectory;
store.Configuration.Storage.Voron.AllowOn32Bits = true;
store.RegisterListener(new UniqueConstraintsStoreListener());
store.Initialize();
With this setup UniqueConstraints are not working on the embedded server.
However, when I put plugins directory to it's default location (WorkingDirectory + /Plugins), it magically starts working. Is it expected behaviour?
More info:
I can reproduce it in Console app and in Web app. In web app, the default location is web root + /Plugins.
After a little bit of investigation I found out that there is a difference in how UniqueConstraints' triggers are registered in store.Configuration.Catalog.Catalogs which might have something to do with the unexpected (for me) behaviour.
With custom PluginDirectory, triggers are registered in store.Configuration.Catalog.Catalogs as BuiltinFitleringCatalog:
When bundle is in the default location, triggers are added to BundlesFilteredCatalog in store.Configuration.Catalog.Catalogs with all other default triggers:
What version of RavenDB?
In RavenDB 3.5 registering plugins on the server-side requires a magic string. Adding this to your example above will probably fix it.
store.Configuration.Settings =
{
{ "Raven/ActiveBundles", "Unique Constraints" }
};

Servicestack, Xamarin and authentication

I've got an ServiceStack service running with custom authentication, this runs fine from the browser and through a Windows console program.
I'm now trying to get a simple Xamarin Android program to authenticate but whatever I try it crashes with an Exception without any further explanation. The code I am using stops at the line with 'var authResponse', I'm using the 4.0.44 ServiceStack packages and the lastest stable Xamarin from inside VS2015.
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// servicestack
var client = new JsonServiceClient("http://10.0.2.2:8080");
var authResponse = client.Get<AuthenticateResponse>( new Authenticate
{
UserName = "Willem",
Password = "secret",
RememberMe = true
});
Any pointers to what/where I should look?
tia
If this is a self-hosted Service you would need to register the HttpListener AppHost to accept requests from different hosts by listening on a host wildcard, e.g:
appHost.Start("http://*:8080/");