SERILOG: Difference between ForContext and and - serilog

I am using the static Logger with the following setup:
Log.Logger = new LoggerConfiguration()
.WriteTo.Seq("http://localhost:5341)
.CreateLogger();
with the following in all my micro-services:
_log = Log.ForContext<GameBase>()
.ForContext("CustomerID", CustomerID);
This code inserts an CustomerID property in each event but not to the message body.
Question: Is there a way to enrich all logs for this context so that the MESSAGE BODY contains this information as well? Like an enricher that would prepend a string to each message body? There are some items I really want to see in the events without having to drill down on each event.
Also, I'm not finding much documentation on the Enrichers. Is there one to not display the full context path?

The message body is configured at the Sink level, usually by defining an outputTemplate (if the Sink supports it, not all of them do). By using the ForContext you are making the CustomerID property available to all messages written to this log instance, but it's on the Sink configuration that you define how this property will be used / shown.
You can see examples in Serilog's documentation under Formatting Output

Related

How to serialise an object as a string in Serilog?

I have a number of logs like this:
Log.Information("Submitting order {#order}", order);
This log goes through RabbitMq -> LogStash -> Elastic and ends up generating a lot of fields (I assume one field for each propery). Eventually I have thousands and thousands of fields in Elastic which brings all kinds of problems.
If I specify the whole object as a parameter, it usually means I don't care much about having all its fields being parsed, I would be more than happy if it was stored as a single string object (but still serlialised as json). Is there a way to customise it in Serilog?
#flaxel's answer works well if you're happy to change the ToString() representation of your object. If you have already overriden ToString() or you don't want it to return a JSON string then consider one of the following options.
If you don't want to log the type as JSON all the time, consider just serializing the object when you log the message. This is the most explicit approach, and allows you to pick and choose which messages have the serialized form and which have the destructured form, but it might make your log statements quite verbose:
// Using Newtonsoft.Json to serialize.
Log.Information("Submitting order {Order}", JsonConvert.SerializeObject(order));
If you always want to serialize a type to JSON, you could register a destructuring policy for that specific type. This keeps your log statements concise and ensures that type is always serialized in the same way:
// When configuring your logger.
Log.Logger = new LoggerConfiguration()
.Destructure.ByTransforming<Order>(order => JsonConvert.SerializeObject(order))
// ...
// This will use the destructurer registered above, so will convert to a JSON string.
Log.Information("Submitting order {#Order}", order);
// These will still use the ToString() method.
Log.Information("Submitting order {Order}", order);
Log.Information("Submitting order {$Order}", order);
Another advantage of this approach is that if you want to change the way you're representing objects of that type, or if you want to revert to the default destructuring approach, you just have to change the policy used when configuring the logger (i.e. the lambda in the snippet above).
If your serialization approach is too complicated to fit in a lambda, or you want to use the same serialization approach for a large number of types, you could define your own IDestructuringPolicy and then register it in a similar way:
class SerializationPolicy : IDestructuringPolicy
{
public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result)
{
// Check type of `value` and serialize if required.
}
}
// When configuring your logger.
Log.Logger = new LoggerConfiguration()
.Destructure.With<SerializationPolicy>()
// ...
I think you can force stringification with the $ operator. And I think you can then override the ToString method to adjust the output. There is also a short example in the documentation of serilog how to force stringification:
var unknown = new[] { 1, 2, 3 }
Log.Information("Received {$Data}", unknown);
And this is the output of the logging function:
Received "System.Int32[]"

How to create a custom log4j2 appender that can write structred data?

Since log4j 1.x is end of life, I want to build my appender in Log4j2 but there is not enough resources nor examples on the net. Additionally, being able to combine it with Messages and custom log levels would be great.
Something like this:
private static final Logger logger = LogManager.getLogger();
logger.log(ACCESS_LOG, new AccessLogMessage("DateTime", "User", "IP", "Data"));
...
try {
...
}
catch(ArithmeticException ex) {
logger.log(EXCEPTION, new ExceptionMessage(ex));
}
A simple custom appender that would write logs to console would be enough for me to get started.
PS: My ultimate goal is to convert the structered log data to json format and send it to my REST service.
An appender that writes to the console already exists. It is called the ConsoleAppender. If you want to format the data in some special way then you would create a custom Layout to do that.
Log4j provides many examples of Layouts. The most common use case is to extend AbstractStringLayout and implement the toSerializable method.

is there a way to overwrite Serilog global logger's log level

the scenario I want is to set the global log level to Error. this is my config code which is called in startup class:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Error()
.WriteTo.Logger(p=>p.Filter.ByIncludingOnly(evt => evt.Level ==
LogEventLevel.Error).WriteTo.MSSqlServer(ConnectionString, "Serilogs",
null, LogEventLevel.Error, 50,
null, null, false, columnOptions))
but the thing is that I want to write some custom Information log in some of my action methods in controllers, like this:
Log.Logger.Information("{User} {RawUrl} ",
userId,actionContext.Request.RequestUri.AbsolutePath);
the problem is that Serilog does not write info logs to SQL table because of the global Error level setting which is defined in startup.cs class. is there any solution for this problem (without setting the global log level to Information)?
The MinimumLevel.Error() construct is intended to be a coarse high level filter which can extremely efficiently rule out logging way before it makes it to a sink. While its natural to want to lean on that, its not critical - you'll be surprised how efficient Serilog will still be if you filter via whitelisting log entries later in the logging pipeline.
WriteTo.Logger and other sinks also provide an equivalent way to set the min level that will go to that sink. The key is to thus only do the filtering at that level (with a minimumLevel optional argument override).
Once you've removed the global filtering, which, by design, is blocking your log request from even getting captured, much less being submitted to the sinks, the next step is to have a way for your Filter.ByIncluding to identify some LogEvent's of Information level as being relevant - one example way is to whitelist particular contexts (but you might also just want to tag it with a property). Example:
Log.Logger = ....
var importantInfoLog = Log.Logger.ForContext<MyClass>();
importantInfoLog.Information("Warning, this is important Information!")
Then you can tell the WriteTo.Logger to
include anything >= Error
include Information if the SourceContext is <MyClass>
An alternate (but in my opinion inferior) solution is to configure multiple WriteTo.Logger:-
with minimumLevel set to Error
the other with minimumLevel left as the default (implying >= Information) but grabbing solely the specific Information level messages that you want.

How to set Object-array default event representation

I'm facing an error while I change the default event representation to Object array in this way:
Configuration configuration = new Configuration();
configuration.getEngineDefaults().getEventMeta().setDefaultEventRepresentation(EventUnderlyingType.OBJECTARRAY);
My events definitions are in create schema way. The epl file get successfully deploy, but when I insert a new Object[] event, an error rise telling that there are no event definition for this event name.
If more details are needed, please, ask for it.
After few tests, I can say that it's necessary to define every event type when the default event representation is set to object array.

BizTalk 2010: Access Context In Map

This could be a very basic question, but hopefully someone will be able to answer it.
I am receiving messages (HL7) using a custom receive pipeline. Inside my custom pipeline, I am promoting properties into the context. I have set up a map where I need to access these properties. However, I would like to access these properties on the send side. The reason why it needs to be on the send side is because I am attaching my map to the send port, so I assume that the message will have already hit the MessageBox and will be mapped on the send side. Hopefully that makes sense...
I know that there are a few 3rd party tools I can use, but I was hoping that there's a simple functoid, or some code I can enter in a scripting functoid that will access the context for me.
Would someone be able to point me in the right direction with this?
There is, indeed a C# functoid that allows access to context properties but it seems to only work with maps on a Receive Port or inside an Orchestration.
You can use the Context Accessor Functiod to do this... Combine it's pipeline component with yours and it should work... Beware it should be handled within the same thread...
http://contextaccessor.codeplex.com/
I don't know if this is possible. However, I had a similar requirement to access message context properties and I was able to populate a message with the context properties in an orchestration thanks to
Greg.Forsythe's excellent instructions
I had a similar situation to access the context properties to get the filename property in the my map. I did the below steps without using any external functoids. Hope this helps someone
Steps:
create a new schema say "FileSchema"
FileNode(rootNode)
-FileName (fieldElement)
Click the schema and in the properties target namespace - clear the namespace.
make the FileName property distinguished. Rt.Click FileName and show promotions and add FileName to Distinguished property tab.
In your target schema, add the field FileName. for me I added it to a SQL schema, since I need the filename for every row in the database
In your orchestration, use the message assignment shape and type the below
// create a variable varFileXML of type System.XML.XMLDocument
// I'm creating a xml same like the file schema and loading that to the XML variable and then assigning that to the Message of type FileSchema
varFileXML = new System.Xml.XmlDocument();
varFileXML.LoadXml("<FileNode><FileName>FileName_0</FileName></FileNode>");
Msg_FileSchema = varFileXML;
//Get the FileName to a variable of string type
varFileName = Msg_FlatFileSchema(FILE.ReceivedFileName);
varFileName = System.IO.Path.GetFileName(varFileName);
//Access the filename property from the message and assign the variable to that
Msg_File.FileName = varFileName;
Now that we got the FileName in to the message you can use that in mapping to your target schema
I used a transform shape to create a new inline map with source as your target schema and fileschema together and the destination as the target schema.I mapped the filename from the fileschema to my target schema the filename property
this is one of the many ways to get the context property. Hope it helps
thanks & regards
Silam

Resources