is there a way to overwrite Serilog global logger's log level - asp.net-mvc

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.

Related

SERILOG: Difference between ForContext and and

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

ODataModel passing "expand" parameter in read

I'd like to pass expand parameters to read because it doesn't work if I call the service like this:
oModel1.read("/LinesSet?$expand=ToCells", {
The read API awaits a map of options as a second argument in which we can define any query using the property urlParameters:
oModel1.read("/LinesSet", {
urlParameters: {
"$expand": "ToCells",
"$select": "LineID,ToCells/CellID,...", // reduce data load
},
filters: [ // Filter required from sap/ui/model/Filter
new Filter({/*...*/}), // reduce data load
],
success: this.onSuccess.bind(this),
// ...
});
⚠️ Please note that loading large amounts of data significantly affects memory consumption and UX negatively. This might even lead to crashing the application altogether ultimately. See the section Loading Large Amounts of Data from the documentation.
Whenever you use methods like [...] sap.ui.model.odata.v2.ODataModel#read [...] in application code, your application must not load large amounts of data.
⚠️ read is a low-level API from the application's point of view. There are other APIs and approaches that can help reducing the amount controller code.
Alternative (better) solution
I'd like to emphasize that v2.ODataModel#read is often not required. You can simply make use of the OData Context/ListBinding by assigning the corresponding name of the <NavigationProperty> to the control in XML:
<Table binding="{ToThatRelatedSingleEntity}" items="{ToThatRelatedCollection}" growing="true">
(Note: You might have to add templateShareable to the aggregation binding accordingly as explained in the topic: Lifecycle of Binding Templates)
The binding, not the application, will then prepare a request automatically for you. No need to use an intermediate JSONModel. Same with v4.ODataModel which doesn't even have the read method.
This makes also migrating to OData V4 much easier.

Grails do not commit after a successful service call

I'm performing the following logic on a single Service in grails 2.4.4.
class SampleService {
void process(params1, params2) {
SampleDomain1 sd1 = new SampleDomain1()
sd1.setProperties(params1)
sd1.save()
SampleDomain2 sd2 = new SampleDomain2()
sd2.setProperties(params2)
sd2.save()
}
}
What I understand is that Services are by default transactional. If sd1.save() is successful but sd2.save() is not, it will rollback the changes and will throw an error. While if both are successful, both are committed upon service's exit.
If my understanding is correct, then both of it should already been persisted to the database. However, the problem is: it does not— unless if you explicitly use the flush: true parameter based on my tests using the same set of params1 and params2.
sd1.save(flush: true)
SampleDomain2 sd2 = new SampleDomain2()
sd2.setProperties(params2)
sd2.save(flush: true)
}
Which, by the way is what I am really avoiding (what would be the point setting it as #Transactional). If that's the catch of Hibernate 4 / Grails 2.4, what do I need to do to make my services to commit at every end of a service call again? Do I need to configure any global configuration of Grails? I really need to flush my Domain classes at the end of every service automatically.
Note
I've already assured that the data is correct, including calling .validate() and other checker. Success in performing .save(flush: true) proves that. The problem I found is regarding to the update on Grails 2.4 on its FlushMode. Now, maybe what I really need is a global settings to override this.
If your data is not being flushed to the database layer there are some possibilities that come to mind.
There's some kind of error when trying to save to the database, you can try passing failOnError=true parameter to the .save() calls to see it clearly. (Actually setting this globally is a good idea since silently failing db calls are a migraine)
You are calling this service method from within the same service object. This will not allow the underlying spring declarative transactions to work due to the use of proxies.
You might have annotated some other method in the same service, in which case the default transactional support is no longer available for the remaining un-annotated (is this even a word?) methods.
You might have created the Service somewhere outside of service folder, not quite sure if this can cause an issue since I've never tried it out.
You have failed to sacrifice a goat to the Groovy and Grails Gods and they are messing with your head.
Edit :
I'm going to try to answer the points in your new edit.
Have you tried failOnError? It might be a issue that occurs when both objects are flushed to the DB at once, instead of manually committing them one at a time.
By figuring out a way to auto flush on save, you are going to be bypassing the transactions altogether AFAIK, now if I'm wrong then by all means go for it. But do test it out first before assuming.
Somewhere on my DataSource.groovy configuration, there is this line:
hibernate {
...
singleSession = true // configure OSIV singleSession mode
flush.mode = 'manual' // OSIV session flush mode outside of transactional context
^^^^^^^^^^
}
Which explicit states that every save should be flushed manually. As a solution, I comment out this line. After that, every database transaction now commits every time it exists a Service.

SWFAddress used to change INDIVIDUAL params in the url

I am using SWFAddress in actionscript 3 to control urls for navigation and controls, and while I am able to target and change specific parameters, I feel like I am missing a cleaner and more consistent way of handling it, perhaps even a feature or method I am not aware of.
Say I have a url and I want to change just the second param of def to xyz.
http://localhost/some-page/#/?param1=abc&param2=def&param3=ghi changed to
http://localhost/some-page/#/?param1=abc&param2=xyz&param3=ghi
I currently am doing:
if (SWFAddress.getParameterNames().indexOf("param2") >= 0) {
SWFAddress.setValue(SWFAddress.getPath() + "?"
+ SWFAddress.getQueryString().replace("param2=" + SWFAddress.getParameter("param2"), "param2=xyz"))
Essentially, checking if the param exists, checking what its current value is, then recreating the whole url using base, '?", and query, making sure I replace the the parameter and the parameter's value, making sure I don't miss the equal sign. This get's sloppy, and is error prone if the param exists but is not set to anything, or whether or not there is an equal sign, and a host of other pitfalls.
So, I can not just tell SWFAddress to update that one parameter in the url? A theoretical function of SWFAddress.setParam("param2, "xyz").
Has anyone coded their own method to micro-manipulate SWFAddress and the url, beyond the single function they supply you with of setValue(val:String)?
I think the short answer is no. According to the documentation there is no setParameter to go with the getParameter method. Looking at the code, it seems that the URL is not cached as a property in the class and therefore cannot be manipulated other than via the setValue method which, of course, updates the URL in the browser.
Presumably you're already parsing the URL in your onChange event so you can use the values to set your application state? If so, you shouldn't need to do so again when you come to rebuild the URL prior to updating it from Flash. If you store the deep-link properties on a Model class somewhere you can handle the defaulting, updating, and error checking without needing to resort to String manipulation. You would then rebuild the URL using those properties, a process you could abstract into a method in your Model class if required.
You should also note that the following line is not particularly robust since it will return true for properties such as param22 and sparam2:
if (SWFAddress.getParameterNames().indexOf("param2") >= 0) { }

why read-only access is writing to my db, in GORM?

In my app, I have a code like this:
// 1
Foo.get(123).example = "my example" // as expected, don't change value in db
// 2
Foo.get(123).bars.each { bar ->
bar.value *= -1 // it's changing "value" field in database!! WHY?
}
note: Foo and Bar are tables in my DB
Why is gorm saving in database is second case?
I don't have any save() method in code.
Tks
SOLVED:
I need to use read() to get a readonly session.
(Foo.discard() also works)
Doc: http://grails.org/doc/latest/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html#5.1.1%20Basic%20CRUD
(In the first case, I guess I made mistest)
Both should save, so the first example appears to be a bug. Grails requests run in the context of an OpenSessionInView interceptor. This opens a Hibernate session at the beginning of each request and binds it to the thread, and flushes and closes it at the end of the request. This helps a lot with lazy loading, but can have unexpected consequences like you're seeing.
Although you're not explicitly saving, the logic in the Hibernate flush involves finding all attached instances that have been modified and pushing the updates to the database. This is a performance optimization since if each change had been pushed it would slow things down. So everything that can wait until a flush is queued up.
So the only time you need to explicitly save is for new instances, and when you want to check validation errors.

Resources