Azure Application Insights - recreate the singleton instance with the modified configuration - .net-6.0

I would like to track Sql command texts using Application Insights (Setting/ overriding the value of EnableSqlCommandTextInstrumentation in a running application) on a demand basis through a reloadable configuration. The ConfigureTelemetryModule of Microsoft.ApplicationInsights SDK uses singleton registration and this limits me from using IOptionsSnapshot. Can anyone please suggest me some ideas to override the config value EnableSqlCommandTextInstrumentation at runtime? Thank you.
Program.cs
var builder = WebApplication.CreateBuilder(args);
...
var myOptions = new MyOptions();
var configSection = builder.Configuration.GetSection(MyOptions.Name);
configSection.Bind(myOptions);
builder.Services.Configure<MyOptions>(configSection);
builder.Services
.AddSingleton<ITelemetryInitializer>(_ => new MyTelemetryInitializer(applicationName))
.ConfigureTelemetryModule<DependencyTrackingTelemetryModule>(
(module, _) =>
{
module.EnableSqlCommandTextInstrumentation = myOptions.EnableSqlCommandTextInstrumentation;
})
.AddApplicationInsightsTelemetry(configuration);

Can anyone please suggest me some ideas to override the config value EnableSqlCommandTextInstrumentation at runtime?
AFAIK, we cannot add configuration value run time to EnableSqlCommandTextInstrumentation.
The module.EnableSqlCommandTextInstrumentation has accept the bool value either we can enable or disable the EnableSqlCommandTextInstrumentation.
As per the MS-Doc you have to keep the settings in your host.json file "EnableDependencyTracking": true
Program.cs
builder.Services
.AddSingleton<ITelemetryInitializer>(_ => new MyTelemetryInitializer(applicationName))
.ConfigureTelemetryModule<DependencyTrackingTelemetryModule>(
(module, _) =>
{
module.EnableSqlCommandTextInstrumentation = true;
})
host.json
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": false,
"excludedTypes": "Exception"
},
"dependencyTrackingOptions": {
// Enable the Sql command text instrumentation to true to collect data
"enableSqlCommandTextInstrumentation": true
}
},

Related

How to start GraphQL server when running .net5 integration tests?

I believe I am missing/misunderstanding something fundamental about the way .net5 works. In setting up an integration test environment for my GraphQL API, I am missing the step on how to start the GraphQL server from said test environment.
When I run the main project, the server is started properly and I can navigate to localhost in the browser and successfully execute GraphQL queries/mutations. My goal here is to set up some automated integration tests.
I'm using NUnit as my test runner and am using WebApplicationFactory<Startup> to "start the server" as I understand it.
In my test project, I'm under the impression that WebApplicationFactory<Startup> is supposed to basically use the Startup.cs class from my main project in my test project so that I don't have to duplicate all the settings, configurations, and injected services. Please correct me if that assumption is not correct.
I've pasted the code I think is relevant.
ApiWebApplicationFactory<Startup>
public class ApiWebApplicationFactory<TStartup> : WebApplicationFactory<Startup> where TStartup : class
{
public static IConfigurationRoot Configuration { get; set; }
public ApiWebApplicationFactory()
{
var configBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
Configuration = configBuilder.Build();
}
protected override void ConfigureClient(HttpClient client)
{
base.ConfigureClient(client);
client.BaseAddress = new Uri("https://localhost");
client.Timeout = new TimeSpan(0, 10, 0);
}
// Based on my assumption this class reuses everything in the Startup.cs class
// I don't actually think this is necessary, but thought it was worth trying
// the test with and without this code.
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddType<GraphQLContentItem>()
.AddType<GraphQLFolder>();
});
}
}
OneTimesetUp
[OneTimeSetUp]
public void OneTimeSetUp()
{
_factory = new ApiWebApplicationFactory<Startup>();
_client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.AddScoped<ICacheRepository, MockCache>();
});
}).CreateClient();
var connString = ApiWebApplicationFactory<Startup>.Configuration.GetConnectionString("DefaultConnection");
var options = new DbContextOptionsBuilder<CmsContext>()
.UseMySql(connString, ServerVersion.AutoDetect(connString))
.Options;
_dbContext = new CmsContext(options);
_dbContext.Database.EnsureCreated();
}
Test
[Test]
public async Task Test()
{
// If I set a breakpoint here, I can't navigate to the URL like I'm expecting to
var graphQLHttpClient =
new GraphQLHttpClient(
new GraphQLHttpClientOptions { EndPoint = new Uri("https://localhost/graphql") },
new NewtonsoftJsonSerializer(),
_client);
var request = new GraphQLRequest
{
Query = #"
query GetCurrentSession() {
getCurrentSession() {
id
name
}
}",
OperationName = "GetCurrentSession"
};
// Error is thrown here with "Bad Request"
var response = await graphQLHttpClient.SendQueryAsync<Session>(request);
// Further code is omitted
}
Please let me know if you see what I am missing. Thanks in advance~

Spartacus Storefront Multisite I18n with Backend

We've run into some problems for our MultiSite Spartacus setup when doing I18n.
We'd like to have different translations for each site, so we put these on an API that can give back the messages dependent on the baseSite, eg: backend.org/baseSiteX/messages?group=common
But the Spartacus setup doesn't let us pass the baseSite? We can
pass {{lng}} and {{ns}}, but no baseSite.
See https://sap.github.io/spartacus-docs/i18n/#lazy-loading
We'd could do it by overriding i18nextInit, but I'm unsure how to achieve this.
In the documentation, it says you can use crossOrigin: true in the config, but that does not seem to work. The type-checking say it's unsupported, and it still shows uw CORS-issues
Does someone have ideas for these problems?
Currently only language {{lng}} and chunk name {{ns}} are supported as dynamic params in the i18n.backend.loadPath config.
To achieve your goal, you can implement a custom Spartacus CONFIG_INITIALIZER to will populate your i18n.backend.loadPath config based on the value from the BaseSiteService.getActive():
#Injectable({ providedIn: 'root' })
export class I18nBackendPathConfigInitializer implements ConfigInitializer {
readonly scopes = ['i18n.backend.loadPath']; // declare config key that you will resolve
readonly configFactory = () => this.resolveConfig().toPromise();
constructor(protected baseSiteService: BaseSiteService) {}
protected resolveConfig(): Observable<I18nConfig> {
return this.baseSiteService.getActive().pipe(
take(1),
map((baseSite) => ({
i18n: {
backend: {
// initialize your i18n backend path using the basesite value:
loadPath: `https://backend.org/${baseSite}/messages?lang={{lng}}&group={{ns}}`,
},
},
}))
);
}
}
and provide it in your module (i.e. in app.module):
#NgModule({
providers: [
{
provide: CONFIG_INITIALIZER,
useExisting: I18nBackendPathConfigInitializer,
multi: true,
},
],
/* ... */
})
Note: the above solution assumes the active basesite is set only once, on app start (which is the case in Spartacus by default).

Cypress: cy.window(): Unable to get property values

Goal
Hello, I wish to gather custom property values for a window object of a page using cy.window().
Issue
When using cy.log() jointly with JSON.stringify(), it presents that it does have properties with values; however, when using lodash _.has(), does not have these properties and thereby no value because these properties are not found.
Code
The following Cypress custom command using cy.window() gathers custom window's property
export function cmdCypressWindow($propName: string) {
cy.window()
.its($propName)
.then(($propValue: Cypress.AUTWindow) => {
cy.log('props names:', JSON.stringify(Object.getOwnPropertyNames($propValue), null, 2));
cy.log('props values:', JSON.stringify($propValue, null, 2));
cy.log('VERSION prop:', _.has($propValue, 'VERSION'));
cy.log('HOST prop:', _.has($propValue, 'HOST'));
cy.log('VERSION value:', _.get($propValue, 'VERSION'));
cy.log('HOST value:', _.get($propValue, 'HOST'));
});
}
Passed in for parameter $propName value 'ACT', because I am expecting the page's window object to contain window.ACT["VERSION"].
Using the example code, the log output shows that the page's window does contain property ACT["VERSION"].
However, when accessing this window object, listed properties are unavailable and undefined:
window
- its .ACT
log props names:, [ "__esModule", "VERSION", "HOST", "RulesList", "RulesAddEdit", "AppsList", "AppsOAuth", "AppsAdd" ]
log props values:, { "VERSION": "0.2.11", "HOST": "radmin" }
log VERSION prop:, false
log HOST prop:, false
log VERSION value:
log HOST value:
How do I resolve this? Thank you, all feedback is very much appreciated.
Found part of the solution here:
TypeScript: Find Key / Value in Object (list comprehension?)
Modified the function:
export function cmdCypressWindow($propName: string) {
cy.window()
.its($propName)
.then(($propValue: Cypress.AUTWindow) => {
const actValues: Record<string, string> = {};
Object.keys($propValue).forEach(key => {
// #ts-ignore
if (typeof $propValue[key] !== 'function') {
// #ts-ignore
actValues[key as string] = $propValue[key];
}
});
cy.log(`window.${$propName}`, JSON.stringify(actValues, null, 2));
cy.wrap(actValues);
});
}
Results show that I was able to acquire values from window object:
log props names:, [ "__esModule", "VERSION", "HOST", "RulesList", "RulesAddEdit", "AppsList", "AppsOAuth", "AppsAdd" ]
log window.ACT, { "VERSION": "0.2.11", "HOST": "radmin" }
wrap {version: 0.2.11, host: radmin}

Adjust ElasticsearchSinkOptions.NumberOfShards in SeriLog not working in .Net core

I have a problem when setting NumberOfShards for ElasticSearch while writing log by SeriLog.
I do config for Serilog like this in .Net Core
.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(config.ElasticConnectionUrl))
{
AutoRegisterTemplate = true,
IndexFormat = config.ElasticIndex + "-{0:yyyy.MM.dd}",
NumberOfShards = 2,
NumberOfReplicas = 0
}));
But when I query the setting of the created Index in Kibana, the numberOfShards still 5 (default value). Even for NumberOfReplicas won't affect.
I am using ELK stack to trace logs.
Is anyone khow why?
You can do Serilog configuration in code, or in appSettings.json configuration. If you are doing this:
var loggerConfiguration = new LoggerConfiguration()
.ReadFrom.Configuration(configuration) // <= this reads from config
.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(config.ElasticConnectionUrl))
{
AutoRegisterTemplate = true,
IndexFormat = config.ElasticIndex + "-{0:yyyy.MM.dd}",
NumberOfShards = 2,
NumberOfReplicas = 0
})); // this gets partially ignored
And you have the following configuration file:
"Serilog": {
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "Elasticsearch",
"Args": {
"nodeUris": "http://localhost:9100"
}
}
]
}
If you have left the index format empty in the JSON file, then you'll probably have an index named like "log-stash-{0:YYYY-MM-DD}", but you'll also have the one you've added in code. Same goes for the URIs. You might have two if you've added one in in the JSON sink WriteTo and in the ElasticsearchSinkOptions in code.
Config as JSON or as code. Choose one (sadly). See this for further information: https://github.com/serilog/serilog-sinks-elasticsearch/issues/180
I was looking to have a way to have a default configuration in code, that could potentially be overwritten using the configuration file, since I'm trying to create a generic opinionated IHostBuilder for our company various APIs, but Serilog is playing nicely. My solution is to move the Sinks config out of the Serilog config section, and define it separately, and load it myself, (in the same format - string Name, Args Dictionary<string, string>), and then create the config manually in code.

Change to desiredCapabilities in beforeEach not reflecting

I am trying to record all browser request and responses. This can be done via browsermob-proxy api's.
For this, I have to change desired capabilities and change httpProxy for browser.
In beforeEach at global or file level, I am trying to change this. Though it reflects in browser object, actual browser is not initiated with those settings.
Simple example:
globalhook file
module.exports = {
before : function (done) {
},
beforeEach: function(browser, done){
browser.options.desiredCapabilities = {
"browserName": "chrome",
"proxy": {
"proxyType": "manual",
"httpProxy": "127.0.0.1:" + someport,
"sslProxy": "127.0.0.1:" + someport
},
"javascriptEnabled": true,
"acceptSslCerts": true,
}
done()
},
afterEach: function(browser, done){
//some code
}
after : function (done) {
//some code
},
}
If i change desired capabilities in before hook, chrome browser is taking those changes. Problem is with beforeEach [global, file level].
Further debugging, I've found setCapabilities function is run just before beforeEach Hook.
Could anyone please have a look or suggest if I am doing something wrong.
Thanks for moving the issue here.
The issue is that the desiredCapabilities are applied to a browser session.
beforeEach and afterEach run on the same browser session therefore any change in the desiredCapabilities won't be applied unless the session is restarted.
If you need to dynamically change the desiredCapabilities you have to structure your tests in the different way, e.g. split your tests in separate test classes
First of all, you probably want to use before() hook instead of beforeEach(), since beforeEach() will run before each test case and you won't be able to modify already started browser session. before() will run before browser session is initiated.
To solve your issue you need to define desiredCapabilities property on your test run module and in case you need to set some dynamic values, use before() hook to modify that object:
module.exports = {
desiredCapabilities: {
"browserName": "chrome",
"proxy": {
"proxyType": "manual",
"httpProxy": "127.0.0.1",
"sslProxy": "127.0.0.1"
},
"javascriptEnabled": true,
"acceptSslCerts": true,
},
before: function(client, done) {
this.desiredCapabilities.proxy.sslProxy = '127.0.0.1' + someport;
done();
}
}
I've searched official docs on this feature but couldn't find anything, though we used this in our tests and it worked well.
Update:
Since you only need to change proxy on desiredCapabilities object, there is a little hack:
// global hooks file:
module.exports = {
beforeEach: function(client, done) {
client.options.desiredCapabilities.proxy = {
"proxyType": "manual",
"httpProxy": "127.0.0.1:" + someport,
"sslProxy": "127.0.0.1:" + someport
};
done(client);
}
};
Changing desiredCapabilities object won't work, since reference to original object is already stored somewhere under the hood of Nightwatch. But nothing stops you from overriding proxy property on that object.
After checking sources and tests in Nightwatch I believe this is the only solution for your case.

Resources