Distinguish applications emitting events to AI using Microsoft.Diagnostics.EventFlow - azure-application-insights

I have two services running in Azure Service Fabric. Now, i want to capture some events from both and redirect them to the same instance of AI.
My eventFlowConfig.json looks like this:
{
"inputs": [
{
"type": "EventSource",
"sources": [
{
"providerName": "My-Provider-Name"
}
]
}
],
"outputs": [
{
"type": "ApplicationInsights",
"instrumentationKey": "AI_INSTRUMENTATION_KEY"
}
],
"schemaVersion": "2016-08-11"
}
This works just fine. One problem though: in AI I cannot tell if a particular trace item came from service A or service B. So, i need a way to add some custom property to AI items coming from a particular app (service). Like, "Service A" or "Service B". Ideally, i should also be able to add PartiotionId & ReplicaOrInstanceId of that service instance, but that is a bonus without which i can live.
UPDATE:
As #cijothomas suggested, we can use an ITelemetryInitializer. This works because EventFlow uses TelemetryConfiguration.Active. We can do this in the constructor of our service:
TelemetryConfiguration.Active.TelemetryInitializers.Add(new TelemetryInitializer("Service A", context.TraceId));
And here is the definition for the TelemetryInitializer class:
class TelemetryInitializer : ITelemetryInitializer
{
private readonly string _roleName;
private readonly string _roleInstance;
public TelemetryInitializer(string roleName, string roleInstance)
{
_roleName = roleName;
_roleInstance = roleInstance;
}
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Cloud.RoleName = _roleName;
telemetry.Context.Cloud.RoleInstance = _roleInstance;
}
}

If same ikey is used for multiple apps, they can be distinguished using their cloudRoleInstance. If its not auto-populated, then you can write own telemetry initializer to populate it correctly.
https://learn.microsoft.com/en-us/azure/azure-monitor/app/api-filtering-sampling#add-properties-itelemetryinitializer
item.Context.Cloud.RoleInstance = "service A or B"

Related

Properties Added with Aspects Not in Generated JSON Schema

I'm trying to generate JSON Schemas using Newtonsoft JSON Schema. Regular properties added in my POCO class are added to the schema when it is generated. I'm also using PostSharp Aspects to add properties, but none of those are added to the schema.
This is a Console application targeting .NET 4.7.2.
Here is my Console application:
public class Program
{
private static void Main(string[] args)
{
JSchemaGenerator gen = new JSchemaGenerator();
JSchema schema = gen.Generate(typeof(MyClass));
File.WriteAllText("C:\\Temp\\TestSchema.json", schema.ToString());
}
}
Here is my aspect:
[PSerializable]
public class TestAspect : InstanceLevelAspect
{
[IntroduceMember]
[JsonProperty(Required = Required.Always)]
public string AspectProp { get; set; }
}
And here is my POCO:
[TestAspect]
public class MyClass
{
public int MyProperty { get; set; }
}
Finally, here is the generated schema:
{
"type": "object",
"properties": {
"MyProperty": {
"type": "integer"
}
},
"required": [
"MyProperty"
]
}
The MyProperty property is in the schema, but AspectProp - the property added by the aspect - is not.
When I open the exe in a decompiler, I can see that AspectProp is actually added to MyClass:
I'm not sure if this is a problem with PostSharp or Newtonsoft JSON Schema or if I'm doing something wrong. It seems like this should be possible.
Edit 1: 20 May
I split my solution out into separate projects - one for the Console app, one for the Aspect and one for MyClass. After making sure I was referencing the generated MyClass DLL directly (i.e. not a project reference, I actually removed the project once MyClass was built) it did not make a difference. AspectProp is still not in the schema. Based on this and the serialization suggested below by #dbc, this leads me to believe it is a problem with the Newtonsoft schema generator
Edit 2: 20 May
Per Antonin's Answer below, I created a new ContractResolver:
public class AspectPropertyResolver : DefaultContractResolver
{
public AspectPropertyResolver()
{
SerializeCompilerGeneratedMembers = true;
}
}
And then registered it in my app before calling Generate:
gen.ContractResolver = new AspectPropertyResolver();
Now my schema includes the aspect-generated property.
Newtonsoft.Json has an opt-in feature to serialize compiler-generated properties. See Newtonsoft.Json.Serialization.DefaultContractResolver.SerializeCompilerGeneratedMembers property.

Spring Boot Actuator Health Groups with nested Indicators

I'm trying to configure Actuator's health probes to include checks for an external service that is nested beyond the first level. For example, when calling /actuator/health, these are the available health indicators:
{
"status":"DOWN",
"components":{
"jms":{
"status":"DOWN",
"components":{
"broker1":{
"status":"DOWN",
"details":{
"error":"javax.jms.JMSException: Failed to create session factory"
}
},
"broker2":{
"status":"UP",
"details":{
"provider":"ActiveMQ"
}
}
}
},
"livenessState":{
"status":"UP"
},
"readinessState":{
"status":"UP"
}
},
"groups":[
"liveness",
"readiness"
]
}
Under the jms component, there are two brokers - broker1 and broker2. I can configure Actuator to include jms in the readiness group like:
endpoint:
health:
probes:
enabled: true
enabled: true
show-details: always
group:
readiness:
include: readinessState, jms
But, this will include all brokers in the readiness probe.
When calling /actuator/health/readiness, I get:
{
"status":"DOWN",
"components":{
"jms":{
"status":"DOWN",
"components":{
"broker1":{
"status":"DOWN",
"details":{
"error":"javax.jms.JMSException: Failed to create session factory"
}
},
"broker2":{
"status":"UP",
"details":{
"provider":"ActiveMQ"
}
}
}
},
"readinessState":{
"status":"UP"
}
}
}
Since the readiness probe in Kubernetes will only prevent routing web requests to my pod, I have cases where I can process the request if broker1 is down as long as broker2 is up. Is there a way to configure Actuator to include nested health indicators in a Health Group instead of just the root ones? I tried combinations like broker1, jms.broker1, jms/broker1, jms\broker1 to no avail.
If it is not directly supported through configuration, is there a custom component I could create that would give me the desired behavior. For example, I thought of the possibility of writing a custom CompositeHealthContributor, but I am not sure if I can aggregate existing health indicators. I do not want to replicate the health checks that are already being done.
One other related use case is to consider the service as healthy as long as one of a group of external resources is available. For example, I have an identical broker in two data centers. As long as one of the brokers is available, then my service could be considered healthy. What would be a good approach for this use case?
I believe that as long as you expose nested indicators as Health objects, you will get the desired result:
#Component
public class CustomHealthIndicator implements ReactiveHealthIndicator {
private final List<CustomContributor> customContributors;
public CustomHealthIndicator(List<CustomContributor> customContributors) {
Assert.notNull(customContributors, "At least one contributor must be available");
this.customContributors = customContributors;
}
#Override
public Mono<Health> health() {
return checkServices()
.onErrorResume(ex -> Mono.just(new Health.Builder().down(ex).build()));
}
private Mono<Health> checkServices() {
return Mono.fromSupplier(() -> {
final Builder builder = new Builder();
AtomicBoolean partial = new AtomicBoolean();
this.customContributors.parallelStream()
.forEach(customContributor -> {
final Object status = customContributor.getStatus();
final boolean isAnErrorState = status instanceof Throwable;
partial.set(partial.get() || isAnErrorState);
if(isAnErrorState){
builder.withDetail(customContributor.getName(), new Builder().down().withException((Throwable) status).build());
} else {
builder.withDetail(customContributor.getName(), new Builder().up().withDetail("serviceDetail", status).build());
}
});
builder.status(partial.get() ? Status.DOWN : Status.UP);
return
builder.build();
});
}
}

SQLite data context in AspNetCore app not working

I have been trying to implement data access in my single page web app using the most recent scaffolding in Visual Studio 2019 and Entity Framework Core . I am using SDK version 3.1.100. Everything builds and runs but I am getting a runtime exception claiming "no such table: User". This makes me think that the connection between EF core and the database is good. Could be anything else as I am not having much luck with debugging. My SQLite database file and sqlite3.dll are contained in the project and have properties set to copy to output. The relevant code portions follow:
appsettings.json:
{
"ConnectionStrings": {
"Sqlite": "Data Source=HopeApp.db"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
The following added to ConfigureServices in Startup.cs:
services.AddEntityFrameworkSqlite()
.AddDbContext<DatabaseContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("Sqlite")));
The database context:
public class DatabaseContext: DbContext
{
public DbSet<datUser> User { get; set; }
public DbSet<datIESum> IESum { get; set; }
//
// https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext
//
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options) { }
}
And finally, the controller that attempts to retrieve the data:
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{
private readonly DatabaseContext db;
public UserController(DatabaseContext db)
{
this.db = db;
}
[HttpGet]
public IEnumerable<datUser> Get()
{
List<datUser> users = new List<datUser>();
using (this.db)
{
foreach (var user in this.db.User)
{
users.Append(user);
}
}
return users;
}
}
I examine the SQLite .db file in DB Brower and see the tables are present and verify that data is present. I have checked file permissions so everyone has full control. I don't know what else to check to track this down. Note that most of the examples these days are provided as "code first" instead of "database first" which works better for my application.
Here is a screen shot of HopeApp.db:
The error:
An exception of type 'Microsoft.Data.Sqlite.SqliteException' occurred in
Microsoft.EntityFrameworkCore.Relational.dll but was not handled in user code
SQLite Error 1: 'no such table: User'.
Any ideas or debugging suggestions? Any more information I can provide?
The error mentions that table User can not be found but the database file has table datUser.
The name of the property in the database context determines the expected table name. Either rename it to match, change the database file to match or use the fluent API to map between the two.
More information can be found here: https://learn.microsoft.com/en-us/ef/core/modeling/relational/tables

<AuthorizeView Roles="...."> using my own roles instead of Active Directory groups?

I know the following razor code works for AD groups.
<AuthorizeView Roles="AD_Group1, AD_Group2">
</AuthorizeView>
However, I will need to grant the permission from a json file. In the json file, it defines,
{
"WindowsUserName1" : [ "My own group 1", "My own group 2" ],
"WindowsUserName2" : [ "My own group 2", "My own group 3" ],
....
}
How to let <AuthorizeView> work with the custom defined grouping?
You can define custom policy to create authorization rules for user groups :
Building custom requirement :
public class UserGroupsRequirement : IAuthorizationRequirement
{
public string[] Groups { get; }
public UserGroupsRequirement(string[] groups)
{
Groups = groups;
}
}
Create a handler for requirement. This needs to inherit from AuthorizationHandler<T> where Tis the requirement to be handled :
public class UserGroupsHandler : AuthorizationHandler<UserGroupsRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserGroupsRequirement requirement)
{
var username = context.User.Claims.FirstOrDefault(c=>c.Type==ClaimTypes.Name).Value;
var groups = requirement.Groups;
//read json file and find user's groups and check whether groups inlcude in required groups.
if (true)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Register the policy :
services.AddAuthorization(config =>
{
config.AddPolicy("UserInGroupsAdmin", policy =>
policy.Requirements.Add(new UserGroupsRequirement(new string[] { "group1"})));
});
services.AddSingleton<IAuthorizationHandler, UserGroupsHandler>();
And you can update the AuthorizeView component to use policy :
<AuthorizeView Policy="UserInGroupsAdmin">
<p>You can only see this if you're an admin or superuser.</p>
</AuthorizeView>
You did not mention whether you're using a Blazor WebAssembly or Blazor Server. This distinction is important, especially when Authentication is involved. However, I guess that you're using Blazor Server as it seems to me that you're using WindowsAuthentication, right?
The following are the steps to do it:
Create a class that derives from the the AuthenticationStateProvider, and override its GetAuthenticationStateAsyn method. In this method, you should read the content of your JSON file, do whatever verification you need to do, and then return Task<AuthenticationState>. The AuthenticationState constructor gets a ClaimsPrincipal object that should contains all the claims you may create for selected users.
The GetAuthenticationStateAsyn method is called by both the CascadingAuthenticationState component, and by the AutherizeRouteView, and both cascade the AutheticationState to child components. The AutherizeView has this property:
[CascadingParameter] private Task AuthenticationState { get; set; }
Which is defined in AutherizeViewCore, so you must wrap your UI with CascadingAuthenticationState component if you wish to get the AuthenticationState object. It is advisable to wrap the App Router Component with the CascadingAuthenticationState component, so that the AuthenticationState is entirely available through your app, like this:
<CascadingAuthenticationState>
<Router AppAssembly="typeof(Program).Assembly" Context="routeData">
</Router>
</CascadingAuthenticationState>
Are you going to use AutherizeView to enable a bar-like UI with Log in Log out user buttons a user name label and an icon ? If not, you should not use the AutherizeView component...
Hope this helps...

Is there a way to customize the ObjectMapper used by Spring MVC without returning String?

I have a graph of objects that I'd like to return different views of. I don't want to use Jackson's #JsonViews to implement this. Right now, I use Jackson MixIn classes to configure which fields are shown. However, all my rest methods return a String rather than a type like BusinessCategory or Collection< BusinessCategory >. I can't figure out a way to dynamically configure the Jackson serializer based on what view I'd like of the data. Is there any feature built into Spring to configure which Jackson serializer to use on a per-function basis? I've found posts mentioning storing which fields you want in serialized in thread-local and having a filter send them and another post filtering based on Spring #Role, but nothing addressing choosing a serializer (or MixIn) on a per-function basis. Any ideas?
The key to me thinking a proposed solution is good is if the return type is an object, not String.
Here are the objects in my graph.
public class BusinessCategory implements Comparable<BusinessCategory> {
private String name;
private Set<BusinessCategory> parentCategories = new TreeSet<>();
private Set<BusinessCategory> childCategories = new TreeSet<>();
// getters, setters, compareTo, et cetera
}
I am sending these across the wire from a Spring MVC controller as JSON like so:
#RestController
#RequestMapping("/business")
public class BusinessMVC {
private Jackson2ObjectMapperBuilder mapperBuilder;
private ObjectMapper parentOnlyMapper;
#Autowired
public BusinessMVCfinal(Jackson2ObjectMapperBuilder mapperBuilder) {
this.mapperBuilder = mapperBuilder;
this.parentOnlyMapper = mapperBuilder.build();
parentOnlyMapper.registerModule(new BusinessCategoryParentsOnlyMapperModule());
}
#RequestMapping(value="/business_category/parents/{categoryName}")
#ResponseBody
public String getParentCategories(#PathVariable String categoryName) throws JsonProcessingException {
return parentOnlyMapper.writeValueAsString(
BusinessCategory.businessCategoryForName(categoryName));
}
}
I have configure the serialization in a MixIn which is in turn added to the ObjectMapper using a module.
public interface BusinessCategoryParentsOnlyMixIn {
#JsonProperty("name") String getName();
#JsonProperty("parentCategories") Set<BusinessCategory> getParentCategories();
#JsonIgnore Set<BusinessCategory> getChildCategories();
}
public class BusinessCategoryParentsOnlyMapperModule extends SimpleModule {
public BusinessCategoryParentsOnlyMapperModule() {
super("BusinessCategoryParentsOnlyMapperModule",
new Version(1, 0, 0, "SNAPSHOT", "", ""));
}
public void setupModule(SetupContext context) {
context.setMixInAnnotations(
BusinessCategory.class,
BusinessCategoryParentsOnlyMixIn.class);
}
}
My current solution works, it just doesn't feel very clean.
"categories" : [ {
"name" : "Personal Driver",
"parentCategories" : [ {
"name" : "Transportation",
"parentCategories" : [ ]
} ]
}
Oh yes, I'm using:
spring-boot 1.2.7
spring-framework: 4.1.8
jackson 2.6.3
Others listed here: http://docs.spring.io/spring-boot/docs/1.2.7.RELEASE/reference/html/appendix-dependency-versions.html
In the end, the only process that met my needs was to create a set of view objects which exposed only the fields I wanted to expose. In the grand scheme of things, it only added a small amount of seemingly unnecessary code to the project and made the flow of data easier to understand.

Resources