Heartbeat functionality from AppInsights does not appear in azure portal - c#

I'm trying to get the heartbeat feature of AppInsights sdk to work but I'm having some trouble.
I have a simple app (just the default ASP.net core 2.2 project created by using dotnet new webapp) running on a k8 cluster inside Azure and is configured with the following settings:
public void ConfigureServices(IServiceCollection services)
{
ApplicationInsightsServiceOptions aiOptions
= new ApplicationInsightsServiceOptions();
// Disables adaptive sampling.
aiOptions.EnableAdaptiveSampling = false;
// Disables QuickPulse (Live Metrics stream).
aiOptions.EnableQuickPulseMetricStream = false;
aiOptions.InstrumentationKey = InstrumentationKey;
aiOptions.EnableHeartbeat=true;
services.AddApplicationInsightsTelemetry(aiOptions);
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddApplicationInsightsKubernetesEnricher();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
However, I can't see any properties in Application Insights related to the heartbeat functionality. I can see other stuff like the kubernetes pod name, etc.
Am I missing some configuration?
Thank you.

The Heartbeat feature is enabled by default as of base SDK 2.5.0 and the ability to configure the Heartbeat was added in 2.3.0-beta1.
I would suggest you to modify your startup file like below:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
ApplicationInsightsServiceOptions aiOpts =
new ApplicationInsightsServiceOptions();
aiOpts.EnableHeartbeat = true; // false to disable
services.AddApplicationInsightsTelemetry(aiOpts);
...
}
Also add using Microsoft.ApplicationInsights.AspNetCore.Extensions; in the top of your file.
Configure the Heartbeat feature in code by modifying the IHeartbeatPropertyManager directly. You can do this when you first obtain the property manager via the TelemetryModules.Instance singleton.
foreach (var md in TelemetryModules.Instance.Modules)
{
if (md is IHeartbeatPropertyManager heartbeatPropertyMan)
{
heartbeatPropertyMan.HeartbeatInterval = TimeSpan.FromMinutes(5.0);
heartbeatPropertyMan.ExcludedHeartbeatProperties.Add("osType");
...
Try this and see if it helps.

To query heartbeat events you can use the following KQL query:
customMetrics
| where name == "HeartbeatState"

Related

Masstransit not creating exchange

Good day
I'm solving the problem that exchange is not being automatically created
I have registered Masstransit in net6 net core application using such uri opions (have tried both):
rabbitmq://myurl
rabbitmq://myurl:5672
Registration looks like this:
services.AddMassTransit(mt =>
{
mt.UsingRabbitMq((context, cfg) =>
{
cfg.Host(new Uri(
RabbitMqOptions.RabbitMqUri),
RabbitMqOptions.VHost,
credentials =>
{
credentials.Username(RabbitMqOptions.UserName);
credentials.Password(RabbitMqOptions.Password);
});
cfg.AutoStart = true;
cfg.Publish<IServerNotificationMessage>(e => e.ExchangeType = RabbitMQ.Client.ExchangeType.Direct);
});
});
services.AddMassTransitHostedService();
Debugging publishing code shows that actual port used is 0 and bus control is null and not started
see the print screen
How can I make the bus start? (as I understand cfg.Host returns void, rather than buscontrol, so that it cannot be explicitly started, have specified autostart option, though its still down)
Thank you in advance
A URI is not required to configure MassTransit, you might just simplify your configuration as shown below.
services.AddMassTransit(mt =>
{
mt.UsingRabbitMq((context, cfg) =>
{
cfg.Host(RabbitMqOptions.Host,
RabbitMqOptions.Port,
RabbitMqOptions.VHost,
h =>
{
h.Username(RabbitMqOptions.UserName);
h.Password(RabbitMqOptions.Password);
});
cfg.AutoStart = true;
cfg.Publish<IServerNotificationMessage>(e => e.ExchangeType = RabbitMQ.Client.ExchangeType.Direct);
});
});
services.AddMassTransitHostedService();
The logs should show the bus starting, if they don't, then the hosted service is not being started. Is this an ASP.NET project, or a project using the .NET Generic Host?

How to configure properly Identity core with DI on net.core 3.1?

I started a pet project on ASP.NET core 3.1 created MVC project and a bunch of libraries to work with.
I also created :
User,
Role,
RoleManager
UserManager
Classes which are inherited from AspNetCore.Identity. But I've got a problem while including those classes into DI container. Every time i get this InvalidOpertationException with info: Unable to resolve service for type Microsoft.AspNetCore.Identity.IUserStore while attempting to activate JournalUserManager.
Here's my code sample:
`services.AddIdentityCore<JournalUser>(options =>
{
options.User.RequireUniqueEmail = true;
options.Lockout.MaxFailedAccessAttempts = 3;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
})
.AddRoles<JournalRole>()
.AddRoleManager<JournalRoleManager>()
.AddUserManager<JournalUserManager>();`
Any suggestions how to fix it?
Check out the custom storage providers guide
In the Reconfigure app to use a new storage provider there is this example of setting up the IUserStore and the IRoleStore
public void ConfigureServices(IServiceCollection services)
{
// Add identity types
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddDefaultTokenProviders();
// Identity Services
services.AddTransient<IUserStore<ApplicationUser>, CustomUserStore>();
services.AddTransient<IRoleStore<ApplicationRole>, CustomRoleStore>();
string connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddTransient<SqlConnection>(e => new SqlConnection(connectionString));
services.AddTransient<DapperUsersTable>();
// additional configuration
}

SignalR - Change server timeout response

i've created a SignalR application but when i set the KeepAliveInternal and ClientTimeOutInterval a value in the hub configuration, the application ignore it and always set to "30,000ms" for both. This is my code:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR().AddHubOptions<ActivityHub>(SetConfig);
// Local function to set hub configuration
void SetConfig(HubOptions<ActivityHub> options)
{
options.ClientTimeoutInterval = TimeSpan.FromMinutes(30);
options.KeepAliveInterval = TimeSpan.FromMinutes(15);
}
}
I've read the SignalR Net Core docs and there is no limit for these two properties. The timeout always is "30,000" even i set those to differente values.
when i set the KeepAliveInternal and ClientTimeOutInterval a value in the hub configuration, the application ignore it and always set to "30,000ms" for both.
For SignalR JavaScript client, the default serverTimeoutInMilliseconds value is 30,000 milliseconds (30 seconds). If you set KeepAliveInterval of HubOptions with a value > 30 seconds, but not specify an appropriate value for serverTimeoutInMilliseconds of HubConnection on client side, the connection will be terminated with an error, like below.
To fix it, you can try to set serverTimeoutInMilliseconds of your HubConnection, like below.
var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub")
.configureLogging(signalR.LogLevel.Trace)
.build();
connection.serverTimeoutInMilliseconds = 120000;
Test Result
Note:
In my above test, I configure SignalR hubs with below code snippet, and we can find a ping message is sent automatically per 60s.
hubOptions.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(1);
Please refer to the official documentation for configuring server options
You may try to configure it as following:
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR(hubOptions =>
{
hubOptions.ClientTimeoutInterval = TimeSpan.FromMinutes(30);
hubOptions.KeepAliveInterval = TimeSpan.FromMinutes(15);
});
}
Or for a single hub:
services.AddSignalR().AddHubOptions<MyHub>(options =>
{
options.ClientTimeoutInterval = TimeSpan.FromMinutes(30);
options.KeepAliveInterval = TimeSpan.FromMinutes(15);
});
I had the same problem back then, and what I changed was simple. I changed TimeSpan.FromMinutes to TimeSpan.FromSeconds since in the documentation you can see that those intervals are in seconds.
So my configuration code now is like this:
/// <summary>
/// Adds SignalR.
/// </summary>
private void AddSignalR(IServiceCollection services)
{
services.AddSignalR(hubOptions =>
{
hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(this.azureConfiguration.SignalR.ClientTimeoutInterval);
hubOptions.HandshakeTimeout = TimeSpan.FromSeconds(this.azureConfiguration.SignalR.HandshakeTimeout);
hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(this.azureConfiguration.SignalR.KeepAliveInterval);
hubOptions.EnableDetailedErrors = this.azureConfiguration.SignalR.EnableDetailedErrors;
hubOptions.MaximumReceiveMessageSize = this.azureConfiguration.SignalR.MaximumReceiveMessageSize;
hubOptions.StreamBufferCapacity = this.azureConfiguration.SignalR.StreamBufferCapacity;
}).AddAzureSignalR(azureOptions =>
{
azureOptions.ConnectionCount = this.azureConfiguration.SignalR.ServerConnectionCount;
});
}

Why is ILogger not logging to Azure app insights?

I am testing out the code directly out of here for a console app: https://learn.microsoft.com/en-us/azure/azure-monitor/app/ilogger#
I basically copied the code and pointed it to a new azure app insights instance. However, none of the logs are showing up in app insights. Am I missing anything?
static void Main(string[] args)
{
// Create DI container.
IServiceCollection services = new ServiceCollection();
// Add the logging pipelines to use. We are using Application Insights only here.
services.AddLogging(loggingBuilder =>
{
// Optional: Apply filters to configure LogLevel Trace or above is sent to ApplicationInsights for all
// categories.
loggingBuilder.AddFilter<ApplicationInsightsLoggerProvider>("", LogLevel.Trace);
loggingBuilder.AddApplicationInsights(******);
});
// Build ServiceProvider.
IServiceProvider serviceProvider = services.BuildServiceProvider();
ILogger<Program> logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogCritical("critical message working");
// Begin a new scope. This is optional. Epecially in case of AspNetCore request info is already
// present in scope.
using (logger.BeginScope(new Dictionary<string, object> { { "Method", nameof(Main) } }))
{
logger.LogWarning("Logger is working - warning"); // this will be captured by Application Insights.
}
}
The code is correct, but you are hitting a known issue with ApplicationInsights and Console apps - the app is dying before ApplicationInsights can send the data to the backend. (data is not sent immediately, but batched and sent at intervals.)
Adding a sleep of ~30 secs should help your case.
Thread.Sleep(31000);
In regular console apps, docs suggest doing an explicit flush.
https://learn.microsoft.com/en-us/azure/azure-monitor/app/console#full-example
But in the ILogger case, you don't control the TelemetryClient instance. So your best alternative is to control the channel, and call flush on the channel followed by a small sleep. Modified code is given below.
class Program
{
static void Main(string[] args)
{
// Create DI container.
IServiceCollection services = new ServiceCollection();
var channel = new InMemoryChannel();
services.Configure<TelemetryConfiguration>(
(config) =>
{
config.TelemetryChannel = channel;
}
);
// Add the logging pipelines to use. We are using Application Insights only here.
services.AddLogging(loggingBuilder =>
{
// Optional: Apply filters to configure LogLevel Trace or above is sent to ApplicationInsights for all
// categories.
loggingBuilder.AddFilter<ApplicationInsightsLoggerProvider>("", LogLevel.Trace);
loggingBuilder.AddApplicationInsights("***");
});
// Build ServiceProvider.
IServiceProvider serviceProvider = services.BuildServiceProvider();
ILogger<Program> logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogCritical("critical message working");
// Begin a new scope. This is optional. Epecially in case of AspNetCore request info is already
// present in scope.
using (logger.BeginScope(new Dictionary<string, object> { { "Method", nameof(Main) } }))
{
logger.LogWarning("Logger is working - warning"); // this will be captured by Application Insights.
}
channel.Flush();
Thread.Sleep(1000);
}
}

.Net Core Reset Password Token and Identityserver 4 token invalid

My applications and APIs are protected using IdentityServer 4.
I have a centralized API for user management (registering new users, updating, deleting and resetting passwords). The token generated by this api will be used by identityserver to reset the user's password.
Problem is I always get invalid token error. I know this has nothing to do with url encoding because forgotten password is handled by identity server and the token generated by identity server works fine. The problem is when a token is generated by different api (even on a single machine).
I looked into creating a common data protection provider but I'm unclear how this is done. Basically, how can I have reset password token created by one api accepted by another?
I'm using Asp Identity's usermanager to generate the reset password token:
var token = await _userManager.GeneratePasswordResetTokenAsync(appUser);
This is how my IdentityServer is setup to use Asp Identity:
services
.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Lockout.AllowedForNewUsers = true;
options.Lockout.DefaultLockoutTimeSpan = new System.TimeSpan(12, 0, 0);
options.Lockout.MaxFailedAccessAttempts = int.Parse(Configuration["MaxFailedAttempts"]);
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
...
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
options.Authentication.CookieSlidingExpiration = true;
})
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
options.DefaultSchema = Globals.SCHEMA_IDS;
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
options.DefaultSchema = Globals.SCHEMA_IDS;
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30;
})
.AddProfileService<CustomProfileService>()
.AddSigninCredentialFromConfig(Configuration.GetSection("SigninKeyCredentials"), Logger);
and this is how my UserManagement Api is setup to use Asp Identity:
services.AddTransient<IUserStore<ApplicationUser>, UserStore<ApplicationUser, IdentityRole, ApplicationDbContext>>();
services.AddTransient<IRoleStore<IdentityRole>, RoleStore<IdentityRole, ApplicationDbContext>>();
services.AddTransient<IPasswordHasher<ApplicationUser>, PasswordHasher<ApplicationUser>>();
services.AddTransient<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.AddTransient<IdentityErrorDescriber>();
var identityBuilder = new IdentityBuilder(typeof(ApplicationUser), typeof(IdentityRole), services);
identityBuilder.AddTokenProvider("Default", typeof(DataProtectorTokenProvider<ApplicationUser>));
services.AddTransient<UserManager<ApplicationUser>>();
Had to move on to other issues and just now getting back to this. I ended up solving this by ensuring that all my APIs and IdentityServer instance was configured to use ASP.NET Core Data Protection. I'm using redis as my distributed caching system and so just had to configure each of my api and identityserver and everything is now using the same keys when generating tokens. Below is what I use in each startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSession();
services.Configure<RedisConfiguration>(Configuration.GetSection("redis"));
services.AddDistributedRedisCache(options =>
{
options.Configuration = Configuration.GetValue<string>("redis:host");
});
var redis = ConnectionMultiplexer.Connect(Configuration.GetValue<string>("redis:host"));
services.AddDataProtection()
.PersistKeysToRedis(redis, "DataProtection-Keys")
.SetApplicationName(<application name>);
services.AddTransient<ICacheService, CacheService>();
...
}
and then don't forget to use the session (in APIs):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseAuthentication();
app.UseSession();
...
}
use the session (in IdentityServer):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseIdentityServer();
app.UseSession();
...
}
As this is a 2 months old question you already soled it I guess. My suggestion would be: take a look at the life span of your token. And set it in your startup.cs like this:
services.Configure<DataProtectionTokenProviderOptions>(options =>
{
options.TokenLifespan = TimeSpan.FromHours(tokenlifespan);
});
Hope this will help you out!
I encountered the same "invalid token" issue with IdentityServer4 on ASP.NET Core 3.1.
An answer of Prathamesh Shende on learn.microsoft.com solved it for me:
In the ResetPassword action, the code first needed to be decoded before being passed on to the _userManager.ResetPasswordAsync method. Like so:
var decodedCode = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(model.Code));

Categories