JObject/JToken doesnt Log on NLog - c#

I have a class and some of properties are dynamic, when my class is pass to some action API the method undestand as a JObject, the problem is to Log on NLOG, when I try to do that, my log from my properties Dynamic shows up "[]" example bellow:
myProperty:[
[[]],
[],
[]
]
When I try convert all over my class Objet to JObject(Newtonsoft) all my log will be [] like bellow:
{ "time": "2019-05-13 18:12:16.2224", "level": "DEBUG", "JsonProperties": { "log": [[[[[]],[[]],[[]],[[[[]],[[]],[[]]]],[[[[[[[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]]]]],[[[[]],[[]],[[]],[[]],[[]],[[[[]],[[]],[[]]]],[[[[[]],[[]]],[[[]],[[]]]]],[[[[[]],[[]],[[]]],[[[]],[[]],[[]]]]],[[[[[]],[[]]],[[[]],[[]]]]]]]]]]]],[[[[[]],[[]],[[]],[[[[]],[[]],[[]],[[]]]]]]]] }, "message": "Testando performance no LoggerGenerator.", "log": [[],[]] }
I solved this problem converts JObject to Dictionary and Works perfectly, the problem is overhead to do that.
I need to solve this problem without create my own method and converts to Dictionary
Thanks.

JObject is an IEnumerable and NLog will try to enumerate it.
You can do the following:
logger.Info("Hello {0}", jObject); // No structured logging, becomes string.Format
logger.Info("Hello {$myobj}", jObject); // Structured logging that forces JObject.ToString
logger.Info("Hello {myobj}", jObject.ToString()); // Converts to string upfront
You can also customize how NLog handles special objects (Like JObject) by overriding these:
NLog.Config.ConfigurationItemFactory.Default.ValueFormatter
NLog.Config.ConfigurationItemFactory.Default.JsonConverter

Related

FHIR Client - How do I create a "valueReference"

I am using the NuGet package Hl7.Fhir.R4 to connect to the NHS ERS API. Part of the "CreateReferral" method requires an extension like this:
"extension": [
{
"url": "https://fhir.nhs.uk/STU3/StructureDefinition/Extension-eRS-Shortlist-SearchCriteria-1",
"valueReference": {
"reference": "#ServiceSearchCriteria-1"
}
}
]
I can't seem to find a model class that will create this output. Any ideas?
You should be able to add an extension to an element/resource with the AddExtension method. As parameters, you fill in the extension's url and a value of any FHIR datatype. For your extension, that would be a Reference, which is modelled by the ResourceReference class:
element.AddExtension("https://fhir.nhs.uk/STU3/StructureDefinition/Extension-eRS-Shortlist-SearchCriteria-1",
new ResourceReference("#ServiceSearchCriteria-1"));
When looking at the referral API, it seems to use FHIR STU3, so you may need to change your library to Hl7.Fhir.Stu3 as well to avoid mismatches in the resource's fields and other version differences. The above method should still work for adding your extension though.

Transforming JSON template based on JSON values posted into API

I have got a requirement of building an azure function which transforms data in our COSMOS DB based on the value posted by a calling service.
So consider this JSON template
{
"processExecutionId": 1000,
"processId": "$PID",
"parentProcessId": 10,
"objectNameTag": "$ObjectName",
"objectName": "bbps.fx",
"processStartedOn": "$startedOn",
"processCompletedOn": "$CompletedAt",
"processExecutionStatusId": 2,
"configurationPayload": "Actual Config Payload with replaced values by the orchestrator or payload read service",
"objectMetadata": {
"jsonPayLoadRead": {
"messages": "$Message"
},
"writeToDB": "$IsWrite"
}
}
This is something we in COSMOS corresponding to a key.
So when a requester application posts that Key it has a JSON object in its body like this
{
"processStartedOn": "2022-01-25 10:10:32",
"processCompletedOn": "2022-01-25 10:20:25",
"objectMetadata": {
"jsonPayLoadRead": {
"messages": "Data uploaded"
},
"writeToDB": "True"
}
}
So the method posting parameters for the template expecting a response from the API after replacing those variables in the template with the values.
The JSON template is not always the same structure. Otherwise we can atleast define a class and can deserialize into it. So what ever the key posted our service needs to take the corresponding JSON template from COSMOS and do the transformation.
So dont know how to handle it the best way or is there is any way to handle this process within COSMOS itself rather than trying using C#.
Please share your thoughts
You can always add a TemplateType property in your class so that you know the various options you have and based on that you know what class to use, there are also ways to stream from cosmos directly without using classes at all but it depends a little what you are trying to achieve if you just need to hand over to another app or not then you can stream from cosmos without even knowing what the structure is

AdaptiveCards throws an exception during .NET parsing

I currently encountered the following problem parsing an adaptive card.
This is the card:
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "{{DATE(${$root.AdditionalData['DUE-DATE']},COMPACT)}}",
"wrap": true
}
]
}
This is the card-content:
{
"AdditionalData": {
"DUE-DATE": "2021-09-10T16:29:59Z"
}
}
Code:
c# on .NET Framework 4.7.2 where layout is a string with the above card and content is a string with the above card-content:
AdaptiveCardTemplate template = new AdaptiveCardTemplate(layout);
string cardJson = template.Expand(content);
AdaptiveCardParseResult card = AdaptiveCard.FromJson(cardJson);
And it crashes with:
AdaptiveCards.AdaptiveSerializationException: 'Error reading string. Unexpected token: Undefined. Path 'text', line 1, position 137.'
JsonReaderException: Error reading string. Unexpected token: Undefined. Path 'text', line 1, position 137.
The generated JSON on cardJson looks wrong to me at the text property:
{"type":"AdaptiveCard","$schema":"http://adaptivecards.io/schemas/adaptive-card.json","version":"1.4","body":[{"type":"TextBlock","text":,"wrap":true}]}
I'm using the adaptive cards nuget packages:
AdaptiveCards 2.7.2
AdaptiveCards.Templating 1.2.
Did I encounter a parsing bug? The value for the text property should be 10.9.2021.
In the designer on adaptivecards.io everything works fine for some reason. Does anyone have a fix/workaround?
If you want the literal "text":"10.9.2021" to appear in your cardJson, use "${formatDateTime(AdditionalData['DUE-DATE'], 'd.M.yyyy')}" to generate the required value for your TextBlock:
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "${formatDateTime(AdditionalData['DUE-DATE'], 'd.M.yyyy')}",
"wrap": true
}
]
}
This causes all date formatting to be performed by the AdaptiveCardTemplate and results in:
{
"type":"AdaptiveCard",
"$schema":"http://adaptivecards.io/schemas/adaptive-card.json",
"version":"1.4",
"body":[
{
"type":"TextBlock",
"text":"10.9.2021",
"wrap":true
}
]
}
Demo fiddle #1 here.
If you would prefer "{{DATE(2021-09-10T16:29:59Z, COMPACT)}}" in your cardJson which delegates date formatting to the TextBlock, use:
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "{{DATE(${AdditionalData['DUE-DATE']}, COMPACT)}}",
"wrap": true
}
]
}
Which results in
"text": "{{DATE(2021-09-10T16:29:59Z, COMPACT)}}",
Demo fiddle #2 here.
Notes:
According to the Microsoft documentation:
Use Dot-notation to access sub-objects of an object hierarchy. E.g., ${myParent.myChild}
Use Indexer syntax to retrieve properties by key or items in an array. E.g., ${myArray[0]}
But, when accessing an object property with a hyphen (or some other reserved operator) in its name, it is apparently necessary to use the Indexer syntax ['DUE-DATE'] instead of Dot‑notation to retrieve its value, passing the property name inside a single-quoted string as the indexer.
According to the docs
There are a few reserved keywords to access various binding scopes. ...
"$root": "The root data object. Useful when iterating to escape to parent object",
Thus you do not need to use $root when accessing properties of the currently scoped object (which is, by default, the root) when using Dot-notation. If for whatever reason you need or want to address the root object directly, you may use $root like so:
"text": "${formatDateTime($root.AdditionalData['DUE-DATE'], 'd.M.yyyy')}",
Demo fiddle #3 here.
However, it seems that using $root in combination with {{DATE()}} causes malformed JSON to be generated. I.e.
"text": "{{DATE(${$root.AdditionalData['DUE-DATE']}, COMPACT)}}",
results in "text":, as indicated in your question.
Demo fiddle #4 here.
This looks to be a bug in the framework. Possibly the parser is choking on the sequence of tokens ${$, as your issue somewhat resembles Issue #6026: [Authoring][.NET][Templating] Inconsistency in accessing $root inside a $when property in adaptive card templating which reports a failure to parse "$when": "${$root.UserName != null}" correctly.
You can avoid the problem either by omitting $root entirely, or by wrapping $root.AdditionalData['DUE-DATE'] in an additional formatDateTime() like so:
"text": "{{DATE(${formatDateTime($root.AdditionalData['DUE-DATE'])}, COMPACT)}}",
Resulting in
"text": "{{DATE(2021-09-10T16:29:59.000Z, COMPACT)}}",
Demo fiddle #5 here.
From the documentation page Adaptive Card Templating SDKs: Troubleshooting:
Q. Why date/time in RFC 3389 format e.g "2017-02-14T06:08:00Z" when used with template doesn't works with TIME/DATE functions?
A. .NET sdk nuget version 1.0.0-rc.0 exhibits this behavior. this behavior is corrected in the subsequent releases... Please use formatDateTime() function to format the date/time string to RFC 3389 as seen in this example, or you can bypass TIME/DATE functions, and just use formatDateTime(). For more information on formatDateTime(), please go here.
While this recommendation to use formatDateTime was to fix a problem in 1.0.0-rc.0, the trick also resolves the issue mentioned in note #2 above.

Unspecified type of the property when deserializing an object

I am trying to use OneSignal API. My problem isn't specific to OneSignal but I think that referencing it might make it easier to explain.
OneSignal is an SAAS for delivering notifications to phones and browsers and I am trying to hook it up to my server. The problem is that the request to OneSignal API returns:
{
"id": "458dcec4-cf53-11e3-add2-000c2940e62c",
"recipients": 5
}
or
{
"errors": {
"invalid_player_ids" : ["5fdc92b2-3b2a-11e5-ac13-8fdccfe4d986", "00cb73f8-5815-11e5-ba69-f75522da5528"]
}
}
or
{
"id": "",
"recipients": 0,
"errors": ["All included players are not subscribed"]
}
while I am trying to deserialize the request using DataContracts and DataContractJsonSerializer.
My question is: how do I parse a JSON where a property can be empty, can be an object or an array of strings? Is there a way to do that using just standard DataContract attributes or do I have to write my own serializer? If so, could you point me to some resources or to any tutorial since I couldn't find anything resolving my problem so far?

Exception destructuring in Serilog

Serilog has a convenient way of destructuring objects as shown in this example:
logger.Debug(exception, "This is an {Exception} text", exception);
logger.Debug(exception, "This is an {#Exception} structure", exception);
The first line causes the logger to log an exception as plain text (by calling ToString()), and the second line causes the logger to write exception properties as separate fields. But what about this overload:
logger.Debug(exception, "This is an exception", exception);
This one takes an exception as its first argument, and it is always written as a string. What I would like to make possible is to enable logging exception in a structured way. Is it possible to configure Serilog to achieve this?
UPDATE. I guess this question leads to another aspect of logging exceptions: how can I ensure that messages are enriched with exception properties (so they are logged in a structured way to the rich sinks like Elasticsearch) without writing all exception properties to the rendered text message (so plain text loggers are not filled with huge piles of exception details).
Take a look at Serilog.Exceptions logs exception details and custom properties that are not output in Exception.ToString().
This library has custom code to deal with extra properties on most common exception types and only falls back to using reflection to get the extra information if the exception is not supported by Serilog.Exceptions internally.
Add the NuGet package and then add the enricher like so:
using Serilog;
using Serilog.Exceptions;
ILogger logger = new LoggerConfiguration()
.Enrich.WithExceptionDetails()
.WriteTo.Sink(new RollingFileSink(
#"C:\logs",
new JsonFormatter(renderMessage: true))
.CreateLogger();
Your JSON logs will now be supplemented with detailed exception information and even custom exception properties. Here is an example of what happens when you log a DbEntityValidationException from EntityFramework (This exception is notorious for having deeply nested custom properties which are not included in the .ToString()).
try
{
...
}
catch (DbEntityValidationException exception)
{
logger.Error(exception, "Hello World");
}
The code above logs the following:
{
"Timestamp": "2015-12-07T12:26:24.0557671+00:00",
"Level": "Error",
"MessageTemplate": "Hello World",
"RenderedMessage": "Hello World",
"Exception": "System.Data.Entity.Validation.DbEntityValidationException: Message",
"Properties": {
"ExceptionDetail": {
"EntityValidationErrors": [
{
"Entry": null,
"ValidationErrors": [
{
"PropertyName": "PropertyName",
"ErrorMessage": "PropertyName is Required.",
"Type": "System.Data.Entity.Validation.DbValidationError"
}
],
"IsValid": false,
"Type": "System.Data.Entity.Validation.DbEntityValidationResult"
}
],
"Message": "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.",
"Data": {},
"InnerException": null,
"TargetSite": null,
"StackTrace": null,
"HelpLink": null,
"Source": null,
"HResult": -2146232032,
"Type": "System.Data.Entity.Validation.DbEntityValidationException"
},
"Source": "418169ff-e65f-456e-8b0d-42a0973c3577"
}
}
Serilog.Exceptions supports the .NET Standard and supports many common exception types without reflection but we'd like to add more, so please feel free to contribute.
Top Tip - Human Readable Stack Traces
You can use the Ben.Demystifier NuGet package to get human readable stack traces for your exceptions or the serilog-enrichers-demystify NuGet package if you are using Serilog.
There's a forum thread discussing this, in which a couple of solutions are presented. Thomas Bolon has created an 'exception destructuring' extension you can find in a Gist.
In this case you use only this syntax:
logger.Debug(exception, "This is an exception");
There's no need to add the exception into the format string.
To ensure the exception is printed to text sinks, just make sure {Exception} is included in the output template. The standard built-in ones already have this, e.g.:
outputTemplate: "{Timestamp} [{Level}] {Message}{NewLine}{Exception}";
This should be avoided altogether. Both ElasticSearch and Serilog aren't designed with the idea in mind that you will be serializing arbitrary objects. Logging objects with the conflicting shapes will result in mapping exceptions in ElasticSearch. If you are using the ElasticSearch sink in NuGet anything that results in a mapping conflict will be lost. Also Serilog does not handle Cyclical relationships so this will result in depth limiter selflog errors. There is a project that attempts to address this by destructuring into dictionaries and passing this to Serilog but you will still wind up with messy logs and mapping exceptions.
Serilog: https://nblumhardt.com/2016/02/serilog-tip-dont-serialize-arbitrary-objects/
I've found it best to be specific about logging exception properties based on what you find useful in the exception.

Categories

Resources