diff --git a/esp/scm/ws_logaccess.ecm b/esp/scm/ws_logaccess.ecm index 71717e9ada4..bd214a668fa 100644 --- a/esp/scm/ws_logaccess.ecm +++ b/esp/scm/ws_logaccess.ecm @@ -25,7 +25,9 @@ ESPenum LogAccessType : int BySourceInstance(5, "BySourceInstance"), BySourceNode(6, "BySourceNode"), ByFieldName(7, "ByFieldName"), - ByPod(8, "ByPod") + ByPod(8, "ByPod"), + ByTraceID(9, "ByTraceID"), + BySpanID(10, "BySpanID") }; ESPenum LogAccessLogFormat : int @@ -67,7 +69,9 @@ ESPenum LogColumnType : string processid("processid"), threadid("threadid"), timestamp("timestamp"), - pod("pod") + pod("pod"), + traceid("traceid"), + spanid("spanid") }; ESPenum LogColumnValueType : string @@ -191,7 +195,9 @@ ESPenum SortColumType : int BySourceInstance(5, "BySourceInstance"), BySourceNode(6, "BySourceNode"), ByFieldName(7, "ByFieldName"), - ByPod(8, "ByPod") + ByPod(8, "ByPod"), + ByTraceID(9, "ByTraceID"), + BySpanID(10, "BySpanID") }; ESPStruct SortCondition @@ -222,7 +228,7 @@ ESPResponse GetLogsResponse [min_ver("1.02")] unsigned int TotalLogLinesAvailable; }; -ESPservice [auth_feature("WsLogAccess:READ"), version("1.05"), default_client_version("1.05"), exceptions_inline("xslt/exceptions.xslt")] ws_logaccess +ESPservice [auth_feature("WsLogAccess:READ"), version("1.06"), default_client_version("1.06"), exceptions_inline("xslt/exceptions.xslt")] ws_logaccess { ESPmethod GetLogAccessInfo(GetLogAccessInfoRequest, GetLogAccessInfoResponse); ESPmethod GetLogs(GetLogsRequest, GetLogsResponse); diff --git a/esp/services/ws_logaccess/WsLogAccessService.cpp b/esp/services/ws_logaccess/WsLogAccessService.cpp index 97c035f6ab0..0e8b12c329d 100644 --- a/esp/services/ws_logaccess/WsLogAccessService.cpp +++ b/esp/services/ws_logaccess/WsLogAccessService.cpp @@ -164,7 +164,14 @@ ILogAccessFilter * buildLogFilterByFields(CLogAccessType searchByCategory, const case CLogAccessType_BySourceNode: { return getHostLogAccessFilter(searchByValue); - break; + } + case CLogAccessType_ByTraceID: + { + return getTraceIDLogAccessFilter(searchByValue); + } + case CLogAccessType_BySpanID: + { + return getSpanIDLogAccessFilter(searchByValue); } case CLogAccessType_ByFieldName: { @@ -343,6 +350,12 @@ bool Cws_logaccessEx::onGetLogs(IEspContext &context, IEspGetLogsRequest &req, I case CSortColumType_BySourceNode: mappedField = LOGACCESS_MAPPEDFIELD_host; break; + case CSortColumType_ByTraceID: + mappedField = LOGACCESS_MAPPEDFIELD_traceid; + break; + case CSortColumType_BySpanID: + mappedField = LOGACCESS_MAPPEDFIELD_spanid; + break; case CSortColumType_ByFieldName: if (isEmptyString(condition.getColumnName())) throw makeStringExceptionV(-1, "WsLogAccess: SortByFieldName option requires ColumnName!"); diff --git a/system/jlib/jlog.cpp b/system/jlib/jlog.cpp index 6039e30ba71..4747c853759 100644 --- a/system/jlib/jlog.cpp +++ b/system/jlib/jlog.cpp @@ -3194,6 +3194,16 @@ ILogAccessFilter * getJobIDLogAccessFilter(const char * jobId) return new FieldLogAccessFilter(jobId, LOGACCESS_FILTER_jobid); } +ILogAccessFilter * getTraceIDLogAccessFilter(const char * traceId) +{ + return new FieldLogAccessFilter(traceId, LOGACCESS_FILTER_trace); +} + +ILogAccessFilter * getSpanIDLogAccessFilter(const char * spanId) +{ + return new FieldLogAccessFilter(spanId, LOGACCESS_FILTER_span); +} + ILogAccessFilter * getColumnLogAccessFilter(const char * columnName, const char * value) { return new ColumnLogAccessFilter(columnName, value, LOGACCESS_FILTER_column); diff --git a/system/jlib/jlog.hpp b/system/jlib/jlog.hpp index 02d5dfff2e6..29c37c4164b 100644 --- a/system/jlib/jlog.hpp +++ b/system/jlib/jlog.hpp @@ -1382,6 +1382,8 @@ typedef enum LOGACCESS_FILTER_host, LOGACCESS_FILTER_column, LOGACCESS_FILTER_pod, + LOGACCESS_FILTER_trace, + LOGACCESS_FILTER_span, LOGACCESS_FILTER_unknown } LogAccessFilterType; @@ -1403,6 +1405,10 @@ inline const char * logAccessFilterTypeToString(LogAccessFilterType field) return "instance"; case LOGACCESS_FILTER_host: return "host"; + case LOGACCESS_FILTER_trace: + return "trace"; + case LOGACCESS_FILTER_span: + return "span"; case LOGACCESS_FILTER_or: return "OR"; case LOGACCESS_FILTER_and: @@ -1431,6 +1437,10 @@ inline unsigned logAccessFilterTypeFromName(char const * name) return LOGACCESS_FILTER_pod; if(strieq(name, "instance")) return LOGACCESS_FILTER_instance; + if(strieq(name, "trace")) + return LOGACCESS_FILTER_trace; + if(strieq(name, "span")) + return LOGACCESS_FILTER_span; if(strieq(name, "host")) return LOGACCESS_FILTER_host; if(strieq(name, "OR")) @@ -1481,6 +1491,8 @@ enum LogAccessMappedField LOGACCESS_MAPPEDFIELD_instance, LOGACCESS_MAPPEDFIELD_pod, LOGACCESS_MAPPEDFIELD_host, + LOGACCESS_MAPPEDFIELD_traceid, + LOGACCESS_MAPPEDFIELD_spanid, LOGACCESS_MAPPEDFIELD_unmapped }; @@ -1680,6 +1692,8 @@ extern jlib_decl ILogAccessFilter * getHostLogAccessFilter(const char * host); extern jlib_decl ILogAccessFilter * getJobIDLogAccessFilter(const char * jobId); extern jlib_decl ILogAccessFilter * getComponentLogAccessFilter(const char * component); extern jlib_decl ILogAccessFilter * getPodLogAccessFilter(const char * podName); +extern jlib_decl ILogAccessFilter * getTraceIDLogAccessFilter(const char * traceId); +extern jlib_decl ILogAccessFilter * getSpanIDLogAccessFilter(const char * spanId); extern jlib_decl ILogAccessFilter * getAudienceLogAccessFilter(MessageAudience audience); extern jlib_decl ILogAccessFilter * getClassLogAccessFilter(LogMsgClass logclass); extern jlib_decl ILogAccessFilter * getBinaryLogAccessFilter(ILogAccessFilter * arg1, ILogAccessFilter * arg2, LogAccessFilterType type); diff --git a/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.cpp b/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.cpp index 92a92d3931f..3c3fd413d20 100644 --- a/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.cpp +++ b/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.cpp @@ -672,6 +672,38 @@ void AzureLogAnalyticsCurlClient::populateKQLQueryString(StringBuffer & queryStr DBGLOG("%s: Searching log entries by class: '%s'...", COMPONENT_NAME, queryValue.str()); break; } + case LOGACCESS_FILTER_trace: + { + if (m_traceSearchColName.isEmpty()) + throw makeStringExceptionV(-1, "%s: 'Trace' log entry field not configured", COMPONENT_NAME); + + queryField = m_traceSearchColName.str(); + + if (!m_traceIndexSearchPattern.isEmpty()) + { + throwIfMultiIndexDetected(queryIndex.str(), m_traceIndexSearchPattern.str()); + queryIndex = m_traceIndexSearchPattern.str(); + } + + DBGLOG("%s: Searching log entries by traceid: '%s'...", COMPONENT_NAME, queryValue.str()); + break; + } + case LOGACCESS_FILTER_span: + { + if (m_spanSearchColName.isEmpty()) + throw makeStringExceptionV(-1, "%s: 'Span' log entry field not configured", COMPONENT_NAME); + + queryField = m_spanSearchColName.str(); + + if (!m_spanIndexSearchPattern.isEmpty()) + { + throwIfMultiIndexDetected(queryIndex.str(), m_spanIndexSearchPattern.str()); + queryIndex = m_spanIndexSearchPattern.str(); + } + + DBGLOG("%s: Searching log entries by spanid: '%s'...", COMPONENT_NAME, queryValue.str()); + break; + } case LOGACCESS_FILTER_audience: { if (m_audienceSearchColName.isEmpty()) diff --git a/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.hpp b/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.hpp index edc0c3d84cb..1f7e9d040b5 100644 --- a/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.hpp +++ b/system/logaccess/Azure/LogAnalytics/CurlClient/AzureLogAnalyticsCurlClient.hpp @@ -69,6 +69,13 @@ class AzureLogAnalyticsCurlClient : public CInterfaceOf StringBuffer m_componentsLookupKeyColumn; StringBuffer m_instanceLookupKeyColumn; + + StringBuffer m_spanSearchColName; + StringBuffer m_spanIndexSearchPattern; + + StringBuffer m_traceSearchColName; + StringBuffer m_traceIndexSearchPattern; + bool targetIsContainerLogV2 = false; public: diff --git a/system/logaccess/ElasticStack/ElasticStackLogAccess.cpp b/system/logaccess/ElasticStack/ElasticStackLogAccess.cpp index 9724a6c10ad..0540845764c 100644 --- a/system/logaccess/ElasticStack/ElasticStackLogAccess.cpp +++ b/system/logaccess/ElasticStack/ElasticStackLogAccess.cpp @@ -560,6 +560,12 @@ void ElasticStackLogAccess::esSearchMetaData(std::string & search, const LogAcce case LOGACCESS_MAPPEDFIELD_host: sortByFieldName = m_hostSearchColName.str(); break; + case LOGACCESS_MAPPEDFIELD_traceid: + sortByFieldName = m_traceSearchColName.str(); + break; + case LOGACCESS_MAPPEDFIELD_spanid: + sortByFieldName = m_spanSearchColName.str(); + break; case LOGACCESS_MAPPEDFIELD_unmapped: default: sortByFieldName = condition.fieldName.get(); @@ -630,6 +636,40 @@ void ElasticStackLogAccess::populateESQueryQueryString(std::string & queryString DBGLOG("%s: Searching log entries by jobid: '%s'...", COMPONENT_NAME, queryValue.str() ); break; } + case LOGACCESS_FILTER_trace: + { + if (m_traceSearchColName.isEmpty()) + throw makeStringExceptionV(-1, "%s: 'traceid' log entry field not configured", COMPONENT_NAME); + + queryField = m_traceSearchColName.str(); + + if (!m_traceIndexSearchPattern.isEmpty()) + { + if (!queryIndex.empty() && queryIndex != m_traceIndexSearchPattern.str()) + throw makeStringExceptionV(-1, "%s: Multi-index query not supported: '%s' - '%s'", COMPONENT_NAME, queryIndex.c_str(), m_workunitIndexSearchPattern.str()); + queryIndex = m_traceIndexSearchPattern; + } + + DBGLOG("%s: Searching log entries by traceid: '%s'...", COMPONENT_NAME, queryValue.str() ); + break; + } + case LOGACCESS_FILTER_span: + { + if (m_spanSearchColName.isEmpty()) + throw makeStringExceptionV(-1, "%s: 'spanid' log entry field not configured", COMPONENT_NAME); + + queryField = m_spanSearchColName.str(); + + if (!m_spanIndexSearchPattern.isEmpty()) + { + if (!queryIndex.empty() && queryIndex != m_spanIndexSearchPattern.str()) + throw makeStringExceptionV(-1, "%s: Multi-index query not supported: '%s' - '%s'", COMPONENT_NAME, queryIndex.c_str(), m_workunitIndexSearchPattern.str()); + queryIndex = m_spanIndexSearchPattern; + } + + DBGLOG("%s: Searchingsort log entries by spanid: '%s'...", COMPONENT_NAME, queryValue.str() ); + break; + } case LOGACCESS_FILTER_class: { if (m_classSearchColName.isEmpty()) diff --git a/system/logaccess/ElasticStack/ElasticStackLogAccess.hpp b/system/logaccess/ElasticStack/ElasticStackLogAccess.hpp index 0e78b20d821..922f3706b41 100644 --- a/system/logaccess/ElasticStack/ElasticStackLogAccess.hpp +++ b/system/logaccess/ElasticStack/ElasticStackLogAccess.hpp @@ -65,6 +65,12 @@ class ElasticStackLogAccess : public CInterfaceOf StringBuffer m_hostSearchColName; StringBuffer m_hostIndexSearchPattern; + StringBuffer m_spanSearchColName; + StringBuffer m_spanIndexSearchPattern; + + StringBuffer m_traceSearchColName; + StringBuffer m_traceIndexSearchPattern; + StringBuffer m_defaultDocType; //default doc type to query elasticlient::Client m_esClient;