-
Notifications
You must be signed in to change notification settings - Fork 304
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HPCC-32281 Grafana/Loki logaccess 2nd phase improvements #18900
HPCC-32281 Grafana/Loki logaccess 2nd phase improvements #18900
Conversation
Jira Issue: https://hpccsystems.atlassian.net//browse/HPCC-32281 Jirabot Action Result: |
8363a0e
to
4590538
Compare
fieldValues->setProp(m_messageColumn.name, stream->queryProp(m_messageColumn.name)); | ||
break; | ||
} | ||
case RETURNCOLS_MODE_custom: //not supported yet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to fail and let user know
@@ -605,14 +647,14 @@ bool GrafanaLogAccessCurlClient::fetchLog(LogQueryResultDetails & resultDetails, | |||
//from https://grafana.com/docs/loki/latest/query/log_queries/ | |||
//Adding | json to your pipeline will extract all json properties as labels if the log line is a valid json document. Nested properties are flattened into label keys using the _ separator. | |||
logLineParser.set(" | json log"); //this parses the log entry and extracts the log field into a label | |||
logLineParser.append(" | line_format \"{{.log}}\""); //Formats output line to only contain log label | |||
logLineParser.append(" | line_format \"{{.log | trim}}\""); //Formats output line to only contain log label |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the line_format output doesn't clean up all json new lines
@jeclrsg this change implements the log output format I provided the other day. Let me know if there are any changes needed so I can move forward w/ this PR. Thanks. |
//This drops the stream, and various insert timestamps | ||
|
||
//we're always going to get a stream container, and a the log line... | ||
//the stream container contains unnecessary, and redundant lines | ||
//there's documentation of a 'drop' command whch doesn't work in practice | ||
//online recomendation is to clear those stream entries... | ||
logLineParser.append(" | label_format log=\"\", filename=\"\", namespace=\"\", node_name=\"\", job=\"\"");// app=\"\", component=\"\", container=\"\", instance=\"\"); | ||
logLineParser.append(" | label_format log=\"\", filename=\"\"");//, namespace=\"\", job=\"\""// app=\"\", component=\"\", container=\"\", instance=\"\"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some of the "stream" values are useful, don't clear them
@@ -73,22 +73,24 @@ class GrafanaLogAccessCurlClient : public CInterfaceOf<IRemoteLogAccess> | |||
LogField m_instanceColumn = LogField("instance", true); | |||
LogField m_podColumn = LogField("pod", true); | |||
LogField m_containerColumn = LogField("container", true); | |||
LogField m_messageColumn = LogField("MSG"); | |||
LogField m_messageColumn = LogField("log"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we'll stuff the entire result log line in the message column.
LogField m_logProcIDColumn = LogField("PID"); | ||
LogField m_logThreadIDColumn = LogField("TID"); | ||
LogField m_logDateTimstampColumn = LogField("tsNs"); | ||
//LogField m_logTimestampColumn = LogField("TIME"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't need these until we extract all columns
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that column mapping in the yaml looks correct to me based on the conversation we had last week.
Thanks @jeclrsg can you also review the newly proposed response format? Keep in mind the timestamp we receive from Grafana is an epoch value which can be converted into human readable timestamp:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some issues spotted - see comments.
Owned<IPropertyIterator> fieldsIter = resultLine->getIterator(); | ||
ForEach(*fieldsIter) | ||
{ | ||
const char * prop = resultLine->queryProp(fieldsIter->getPropKey()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const char * prop = fieldsIter>queryPropValue() is more efficient/simpler.
same comments for the other formats below.
encodeXML(prop, returnbuf); | ||
returnbuf.appendf("</%s>", fieldsIter->getPropKey()); | ||
} | ||
returnbuf.appendf("</lin>"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: "line" - would generate invalid xml
firstField = false; | ||
|
||
StringBuffer prop; | ||
resultLine->getProp(fieldsIter->getPropKey(), prop); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also should use queryPropValue()
fieldValues->setProp(m_nodeColumn.name, stream->queryProp(m_nodeColumn.name)); | ||
fieldValues->setProp(m_containerColumn.name, stream->queryProp(m_containerColumn.name)); | ||
fieldValues->setProp(m_instanceColumn.name, stream->queryProp(m_instanceColumn.name)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a comment //fallthrough whenever a case falls through to the next case.
I initially read the code incorrectly because it wasn't there, and coverity will also complain.
case RETURNCOLS_MODE_default: | ||
{ | ||
fieldValues->setProp(m_podColumn.name, stream->queryProp(m_podColumn.name)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//fallthrough
logLineIter.setown(result.getElements("values")); | ||
} | ||
|
||
processValues(returnbuf, logLineIter, result.queryPropTree("stream"), format, retcolmode, isFirstLine); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function will crash if logLineIter is null - either it should not be called, or processValues() needs protecting.
@@ -832,6 +873,24 @@ GrafanaLogAccessCurlClient::GrafanaLogAccessCurlClient(IPropertyTree & logAccess | |||
if (logMap.hasProp(logMapSearchColAtt)) | |||
m_podColumn.name = logMap.queryProp(logMapSearchColAtt); | |||
} | |||
else if (streq(logMapType, "message")) | |||
{ | |||
if (logMap.hasProp(logMapIndexPatternAtt)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code looks like it is duplicated multiple times. A candidate for cleaning up in a later PR.
(Not being commoned up makes it look as though each of the different branches are doing something different..)
@rpastrana No, I don't see any problems in that response formatting either. It might be nice to have the timestamps be consistent coming out of all the engines, either dates or as ints. But I don't think there'd be any problem with adding some code on the client side to check the engine and be prepared to convert to a date. |
Thanks
Thanks for the feedback @jeclrsg.
|
b18c15a
to
9dfd13a
Compare
fieldValues->setProp(m_nodeColumn.name, stream->queryProp(m_nodeColumn.name)); | ||
fieldValues->setProp(m_containerColumn.name, stream->queryProp(m_containerColumn.name)); | ||
fieldValues->setProp(m_instanceColumn.name, stream->queryProp(m_instanceColumn.name)); | ||
[[fallthrough]]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C17 feature, not sure if there's any concern using c17 specific constructs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I hadn't picked up on that feature. We require c++17, so no problem using the feature - it has the advantage it will be checked by the compiler.
if (result.hasProp("values")) | ||
{ | ||
logLineIter.setown(result.getElements("values")); // if no values elements found, will get NullPTreeIterator | ||
if (!logLineIter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, this will always be non-null. You have to check the result of logLineIter->first() to know if there are no elements found.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k, the goal was to protect processValues from null logLineIter, removing in favor of L271
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rpastrana looks good. Agree that one line is redundant. Please squash.
if (result.hasProp("values")) | ||
{ | ||
logLineIter.setown(result.getElements("values")); // if no values elements found, will get NullPTreeIterator | ||
if (!logLineIter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, this will always be non-null. You have to check the result of logLineIter->first() to know if there are no elements found.
e75057f
to
e0c6ada
Compare
@ghalliday squashed |
@jeclrsg what needs to be done on the UI side to support these changes? The "epoch" column type is the only one I can think of. |
@rpastrana looks good. Only question is should this target 9.6.x? |
- Ensures proper log output format - Utilizes context log fields available - Updates grafana logaccess sample config - Replaces unnecessary funtion w/ inline logic - Removes invalid wildcard filter restriction - Trims log data at server due to added json newlines - Reenables several stream columns - Adds epoch log field type - Ensures logfield type default is set to string Signed-off-by: Rodrigo Pastrana <[email protected]>
e0c6ada
to
a0283eb
Compare
@ghalliday rebased to 9.6.x but had trouble building locally, awaiting build results on gihubactions... |
Jira Issue: https://hpccsystems.atlassian.net//browse/HPCC-32281 Jirabot Action Result: |
Type of change:
Checklist:
Smoketest:
Testing: