Set custom filters for boost log sink with custom attribute & severity level - filtering

I have a log setup in which I have 2 types of log messages:
1 based solely on severity level
1 based solely on a custom tag attribute
These attributes are defined as follows:
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
I want to create a filter function that allows a message to be added to my log based on either of the 2 criteria (note that the log messages based on the custom tag attribute are always printed with severity level info, based on the trivial logger's severity levels).
So I want to have a filter, which allows a message based on if a message has the custom tag, and if it does not have it, based on the severity of the message.
I have tried to have a relative simple filter which does the following:
sink_->set_filter(
trivial::severity >= severityLevel
|| (expr::has_attr(tag_attr) && tag_attr == "JSON" && logJson_)
);
But as it is possible that the severityLevel can be either Debug, Info, Warning, Error or Fatal, if the level is configured as either Debug or Info, the custom tag attribute is ignored by the filter.
I have tried using a c++11 lambda, as following:
sink_->set_filter([this, severityLevel](const auto& attr_set) {
if (<condition for custom tag first>) {
return true;
} else if (<condition for severity level second>) {
return true;
} else {
return false;
}
});
But then I don't have an idea on how to actually check for my conditions. I have tried the following:
if (attr_set["Tag"].extract<std::string>() == "JSON" && logJson_) {
return true;
} else if (attr_set["Severity"].extract<trivial::severity_level>() >= severityLevel) {
return true;
} else {
return false;
}
But the compiler throws several errors about this:
Core/Source/Log/Logger.cpp: In lambda function:
Core/Source/Log/Logger.cpp:127:48: error: expected primary-expression before '>' token
if (attr_set["Tag"].extract<std::string>() == "JSON" && logJson_) {
^
Core/Source/Log/Logger.cpp:127:50: error: expected primary-expression before ')' token
if (attr_set["Tag"].extract<std::string>() == "JSON" && logJson_) {
^
Core/Source/Log/Logger.cpp:129:72: error: expected primary-expression before '>' token
} else if (attr_set["Severity"].extract<trivial::severity_level>() >= severityLevel) {
^
Core/Source/Log/Logger.cpp:129:74: error: expected primary-expression before ')' token
} else if (attr_set["Severity"].extract<trivial::severity_level>() >= severityLevel) {
^
Core/Source/Log/Logger.cpp: In lambda function:
Core/Source/Log/Logger.cpp:134:5: error: control reaches end of non-void function [-Werror=return-type]
});
^
cc1plus: all warnings being treated as errors
scons: *** [obj/release/Core/Source/Log/Logger.os] Error 1
====5 errors, 0 warnings====
I have been scouring the boost log documentation about extracting the attributes myself, but I cannot find the information I need.
EDIT:
For posterity, I'll add how I've solved my issue (with thanks to the given answer by Andrey):
sink_->set_filter([this, severityLevel](const auto& attr_set) {
if (attr_set[tag_attr] == "JSON") {
return logJson_;
} else if (attr_set[severity] >= severityLevel) {
return true;
} else {
return false;
}
});

The filter can be written in multiple ways, I will demonstrate a few alternatives.
First, using expression templates you can write it this way:
sink_->set_filter(
(expr::has_attr(tag_attr) && tag_attr == "JSON" && logJson_) ||
trivial::severity >= severityLevel
);
Following the normal short-circuiting rules of C++, the tag attribute will be tested first and if that condition succeeds, the severity will not be tested. If the tag is not present or not JSON or logJson_ is not true, then severity level is tested.
Note that the filter above will save copies of its arguments (including logJson_ and severityLevel) at the point of construction, so if you change logJson_ later on the filter will keep using the old value. This is an important difference from your later attempts with C++14 lambdas, which access logJson_ via the captured this pointer. If you actually want to save a reference to your member logJson_ in the filter, you can use phoenix::ref:
sink_->set_filter(
(expr::has_attr(tag_attr) && tag_attr == "JSON" && boost::phoenix::ref(logJson_)) ||
trivial::severity >= severityLevel
);
However, you should remember that the filter can be called concurrently in multiple threads, so the access to logJson_ is unprotected. You will have to implement your own thread synchronization if you want to update logJson_ in run time.
Barring multithreading issues, your second attempt with a lambda is almost correct. The compiler is complaining because the lambda function is a template, and the result of attr_set["Tag"] expression depends on one of the template parameters (namely, the type of attr_set). In this case, the programmer has to qualify that the following extract<std::string>() expression is a template instantiation and not a sequence of comparisons. This is done by adding a template keyword:
if (attr_set["Tag"].template extract<std::string>() == "JSON" && logJson_) {
return true;
} else if (attr_set["Severity"].template extract<trivial::severity_level>() >= severityLevel) {
return true;
} else {
return false;
}
Note that you could use a standalone function to the same effect, which wouldn't require the template qualification:
if (boost::log::extract<std::string>("Tag", attr_set) == "JSON" && logJson_) {
return true;
} else if (boost::log::extract<trivial::severity_level>("Severity", attr_set) >= severityLevel) {
return true;
} else {
return false;
}
Finally, the preferred way to extract attribute values is to leverage attribute keywords, which you declared previously. Not only this allows to avoid the template qualification quirk but it also removes a lot of code duplication.
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(tag_attr, "Tag", std::string)
if (attr_set[tag_attr] == "JSON" && logJson_) {
return true;
} else if (attr_set[severity] >= severityLevel) {
return true;
} else {
return false;
}
The attribute value name and type are inferred from the keyword declaration in this case. This use of attribute keywords is documented at the end of this section.

Related

Backtracking results in same repeating course

I am trying to solve a puzzle, and it has been suggested that I use backtracking - I did not know the term so did some investigation, and found the following in Wikipedia:
In order to apply backtracking to a specific class of problems, one must provide the data P for the particular instance of the problem that is to be solved, and six procedural parameters, root, reject, accept, first, next, and output. These procedures should take the instance data P as a parameter and should do the following:
root(P): return the partial candidate at the root of the search tree.
reject(P,c): return true only if the partial candidate c is not worth completing.
accept(P,c): return true if c is a solution of P, and false otherwise.
first(P,c): generate the first extension of candidate c.
next(P,s): generate the next alternative extension of a candidate, after the extension s.
output(P,c): use the solution c of P, as appropriate to the application.
The backtracking algorithm reduces the problem to the call backtrack(root(P)), where backtrack is the following recursive procedure:
procedure backtrack(c) is
if reject(P, c) then return
if accept(P, c) then output(P, c)
s ← first(P, c)
while s ≠ NULL do
backtrack(s)
s ← next(P, s)
I have attempted to use this method for my solution, but after the method finds a rejected candidate it just starts again and finds the same route, rather than the next possible one.
I now don't think I have used the next(P,s) correctly, because I don't really understand the wording 'after the extension s'.
I've tried 2 methods:
(a) in the first() function, generating all possible extensions, storing them in a list, then using the first. The next() function then uses the other extensions from the list in turn. But this maybe can't work because of the calls to backtrack() in between the calls to next().
(b) adding a counter to the data (i.e. the class that includes all the grid info) and incrementing this for each call of next(). But can't work out where to reset this counter to zero.
Here's the relevant bit of code for method (a):
private PotentialSolution tryFirstTrack(PotentialSolution ps)
{
possibleTracks = new List<PotentialSolution>();
for (Track trytrack = Track.Empty + 1; trytrack < Track.MaxVal; trytrack++)
{
if (validMove(ps.nextSide, trytrack))
{
ps.SetCell(trytrack);
possibleTracks.Add(ps);
}
}
return tryNextTrack(ps);
}
private PotentialSolution tryNextTrack(PotentialSolution ps)
{
if (possibleTracks.Count == 0)
{
ps.SetCell(Track.Empty);
return null;
}
ps = possibleTracks.First();
// don't use same one again
possibleTracks.Remove(ps);
return ps;
}
private bool backtrackTracks(PotentialSolution ps)
{
if (canExit)
{
return true;
}
if (checkOccupiedCells(ps))
{
ps = tryFirstTrack(ps);
while (ps != null)
{
// 'testCells' is a copy of the grid for use with graphics - no need to include graphics in the backtrack stack
testCells[ps.h, ps.w].DrawTrack(g, ps.GetCell());
if (ps.TestForExit(endColumn, ref canExit) != Track.MaxVal)
{
drawRowColTotals(ps);
return true;
}
ps.nextSide = findNextSide(ps.nextSide, ps.GetCell(), ref ps.h, ref ps.w);
if (ps.h >= 0 && ps.h < cellsPerSide && ps.w >= 0 && ps.w < cellsPerSide)
{
backtrackTracks(ps);
ps = tryNextTrack(ps);
}
else
return false;
}
return false;
}
return false;
}
and here's some code using random choices. This works fine, so I conclude that the methods checkOccupiedCells() and findNextSide() are working correctly.
private bool backtrackTracks(PotentialSolution ps)
{
if (canExit)
{
return true;
}
if (checkOccupiedCells(ps))
{
Track track = createRandomTrack(ps);
if (canExit)
return true;
if (track == Track.MaxVal)
return false;
ps.SetCell(track);
ps.nextSide = findNextSide(ps.nextSide, track, ref ps.h, ref ps.w);
if (ps.h >= 0 && ps.h < cellsPerSide && ps.w >= 0 && ps.w < cellsPerSide)
backtrackTracks(ps);
else
return false;
}
}
If it helps, there's more background info in the puzzle itself here

Unable to access cellvalue when using external filter ag-grid with angularjs

I'm using an external filter in ag-grid which is supposed to filter the records based on a select value dropdown which has values corresponding to a specific field in the grid.
And I'm unable to access the value of the field using node.data.fieldName as mentioned in the documentation here.
Below is what I'm doing:
function isExternalFilterPresent() {
return $scope.filterval.ReleaseType!='All' && $scope.filterval.ReleaseType!='';
}
function doesExternalFilterPass(){
console.log('$scope.filterval.ReleaseType : ' ,$scope.filterval.ReleaseType);
if($scope.filterval.ReleaseType == 'A'){return node.data.ReleaseType = 'A';}
if($scope.filterval.ReleaseType == 'B'){}
if($scope.filterval.ReleaseType == 'C'){}
if($scope.filterval.ReleaseType == 'D'){}
if($scope.filterval.ReleaseType == 'D'){}
}
It throws an error : node is not defined
When I try using just data.fieldName it says 'data is not defined'
Can someone please help me understand how I can access the value of the specific field here.
You need to provide node as an argument to the function. ag-grid calls this function with appropriate argument node.
Link: Example External filter
function doesExternalFilterPass(node) { // <- node as argument
console.log('$scope.filterval.ReleaseType : ' ,$scope.filterval.ReleaseType);
if($scope.filterval.ReleaseType == 'A'){return node.data.ReleaseType = 'A';}
if($scope.filterval.ReleaseType == 'B'){}
if($scope.filterval.ReleaseType == 'C'){}
if($scope.filterval.ReleaseType == 'D'){}
if($scope.filterval.ReleaseType == 'D'){}
}

Firestore Security Rules: request.time "undefined on object"

I'm trying to create a Security Rule based upon request.time as given in an example on AngularFirebase website.
My function is
function isThrottled() {
return request.time < resource.data.lastUpdate + duration.value(1, 'm')
}
Where I'm trying to allow update: if isThrottled() == false
However, when I try to update a document with this rule, it fails due to time being not defined on the object.
Error: simulator.rules line [169], column [12]. Property time is
undefined on object.
Shouldn't every request have a time or TimeStamp attached to it? Is this something to do with how I'm initializing my Cloud Functions or client app?
Screenshots below:
EDIT
A snippet for the rest of the update security rules are:
service cloud.firestore {
match /databases/{db}/documents {
match /users/{userId} {
match /username/{id} {
allow update: if isSelf(userId)
&& usernameAvailable(incomingData().username)
&& incomingData().username is string
&& incomingData().username.size() <= 25
&& incomingFields().size() == 1
&& isThrottled() == false;
}
}
function incomingData() {
return request.resource.data
}
function isThrottled() {
return request.time < resource.data.lastUpdate + duration.value(1, 'm')
}
function incomingFields() {
return incomingData().keys()
}
function isSelf(userId) {
return userId == currentUser().uid;
}
function usernameAvailable(username) {
return !exists(/databases/$(db)/documents/usernames/$(username));
}
}
}
The username collection is a subcollection under each user document (in the users root collection. Each username document only has 1 field called username that users can update).
This might not be useful for your case in particular, but I had the same error when checking a custom claim on the token object.
Before accessing the field you can use in to check whether the property exists on the object. This code generates the error if agent is not defined:
allow write: if request.auth != null && request.auth.token.agent == true;
This code works fine if agent is not defined:
allow write: if request.auth != null && "agent" in request.auth.token && request.auth.token.agent == true;

How can I get an alert if a specific url substring is present and nothing if null

function getCode() {
if (window.location.href.indexOf("?discount=")) {
var url = (document.URL);
var id = url.substring(url.lastIndexOf('=') + 1);
window.alert(id);
return true;
} else {
return false;
}
}
Purpose: When people go to our "Service Request" page using a QR code that has a substring of ?discount=1234. I have been testing by creating an alert box with the discount code showing. Eventually I want to be able to populate that "1234" automatically into a "Discount Code:" text field on page load.
The above is a mixture of a few suggestions when I researched it.
Result: Going to example.com/serviceRequest.html?discount=1234 gives me the appropriate alert "1234", as I want... Going to example.com/serviceRequest.html gives me the alert http://example.com/serviceRequest.html, but I don't want anything to happen if "?discount=" is null.
Any suggestions?
indexOf returns -1 if the search pattern doesn't exist. In JavaScript, anything not a 0 or false or undefined is considered true.
So your line of:
if(window.location.href.indexOf("?discount=")) {
Would better search as:
if(window.location.href.indexOf("?discount=") > -1) {
Try changing your if-statement to:
if(window.location.href.indexOf("?discount=") != -1)
Look up the documentation for ".indexOf". It returns -1 for not found and >= 0 if it is found.
...indexOf("?discount=") >= 0
substring and indexOf return -1 if the text is not found, so you can test for this. E.g.
function getCode() {
if(window.location.href.indexOf("?discount=") != -1) {
var url = (document.URL);
var id = url.substring(url.lastIndexOf('=') + 1);
window.alert(id);
return true;
}
else {
return false;
}
}
You just need to test the indexOf value:
function getCode() {
if (window.location.href.indexOf("?discount=") !== -1) {
var url = (document.URL);
var id = url.substring(url.lastIndexOf('=') + 1);
window.alert(id);
return true;
}
else {
return false;
}
}
So the quick and dirty answer would be
var discount = window.location.search.split("?discount=")[1];
alert(discount);
But this doesn't take into account the occurence of other query string parameters.
You'll really want to parse all the query parameters into a hash map.
This article does a good job of showing you a native and jQuery version.
http://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html

tinymce.dom.replace throws an exception concerning parentNode

I'm writing a tinyMce plugin which contains a section of code, replacing one element for another. I'm using the editor's dom instance to create the node I want to insert, and I'm using the same instance to do the replacement.
My code is as follows:
var nodeData =
{
"data-widgetId": data.widget.widgetKey(),
"data-instanceKey": "instance1",
src: "/content/images/icon48/cog.png",
class: "widgetPlaceholder",
title: data.widget.getInfo().name
};
var nodeToInsert = ed.dom.create("img", nodeData);
// Insert this content into the editor window
if (data.mode == 'add') {
tinymce.DOM.add(ed.getBody(), nodeToInsert);
}
else if (data.mode == 'edit' && data.selected != null) {
var instanceKey = $(data.selected).attr("data-instancekey");
var elementToReplace = tinymce.DOM.select("[data-instancekey=" + instanceKey + "]");
if (elementToReplace.length === 1) {
ed.dom.replace(elementToReplace[0], nodeToInsert);
}
else {
throw new "No element to replace with that instance key";
}
}
TinyMCE breaks during the replace, here:
replace : function(n, o, k) {
var t = this;
if (is(o, 'array'))
n = n.cloneNode(true);
return t.run(o, function(o) {
if (k) {
each(tinymce.grep(o.childNodes), function(c) {
n.appendChild(c);
});
}
return o.parentNode.replaceChild(n, o);
});
},
..with the error Cannot call method 'replaceChild' of null.
I've verified that the two argument's being passed into replace() are not null and that their parentNode fields are instantiated. I've also taken care to make sure that the elements are being created and replace using the same document instance (I understand I.E has an issue with this).
I've done all this development in Google Chrome, but I receive the same errors in Firefox 4 and IE8 also. Has anyone else come across this?
Thanks in advance
As it turns out, I was simply passing in the arguments in the wrong order. I should have been passing the node I wanted to insert first, and the node I wanted to replace second.