Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions common/eventconsumption/eventindexhotspot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,17 @@ class CHotspotEventVisitor : public CInterfaceOf<IEventVisitor>
// The lowest and highest bucket number with observed activity.
using Range = std::pair<bucket_type, bucket_type>;
// The observed activity for a single bucket kind.
struct Activity
class Activity
{
public:
Buckets buckets;
Range range{std::numeric_limits<bucket_type>::max(), 0};

Activity() = default;
bool hasActivity() const
{
return !buckets.empty();
}
};
public:
CActivity(CHotspotEventVisitor& _container, __uint64 _id)
Expand All @@ -62,11 +69,13 @@ class CHotspotEventVisitor : public CInterfaceOf<IEventVisitor>
void forEachBucket(IBucketVisitor& visitor)
{
const char* path = container.operation.queryMetaInfoState().queryFilePath(id);
if (!hasActivity()) // suppress inactive files
return;
visitor.arrive(id, path);
for (unsigned kind = 0; kind < NumNodeKinds; kind++)
{
Activity& act = activity[kind];
if (act.range.first != std::numeric_limits<bucket_type>::max() && !act.buckets.empty())
if (act.hasActivity())
{
for (bucket_type bucket = act.range.first; bucket <= act.range.second; bucket++)
{
Expand Down Expand Up @@ -102,6 +111,16 @@ class CHotspotEventVisitor : public CInterfaceOf<IEventVisitor>
return 0;
}

bool hasActivity() const
{
for (const Activity& act : activity)
{
if (act.hasActivity())
return true;
}
return false;
}

private:
CHotspotEventVisitor& container; // back reference to obtain shared configuration
__uint64 id{0};
Expand Down
142 changes: 126 additions & 16 deletions common/eventconsumption/eventindexsummarize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,11 @@ class CTraceCollector : public CSummaryCollector
public: // IEventVisitor
virtual bool visitEvent(CEvent& event) override
{
// Allow EventQueryStart events to pass through; trace ID to service name
// mapping is built earlier by the metadata parser (CMetaInfoState::visitEvent)
if (event.queryType() == EventQueryStart)
return true;

// Implicit event filter applied unconditionally
if (queryEventContext(event.queryType()) != EventCtxIndex)
return true;
Expand All @@ -688,7 +693,7 @@ class CTraceCollector : public CSummaryCollector
virtual void summarize() override
{
// Determine which node kinds have data
bool haveNodeKindEntries[NumNodeKinds] = {false,};
std::fill_n(haveNodeKindEntries, NumNodeKinds, false);
for (Cache::value_type& entry : stats)
{
TraceStats& traceStats = entry.second;
Expand All @@ -701,23 +706,18 @@ class CTraceCollector : public CSummaryCollector
}
}
}
if (summarization == IndexSummarization::byService)
summarizeByService();
else
summarizeByTrace();
}

protected:
void summarizeByTrace()
{
StringBuffer line;
appendCSVColumns(line, "Trace ID", "Service Name", "First Timestamp", "Last Timestamp", "Unique Files");

// Add aggregate statistics headers
appendNodeKindHeaders(line, "Total ");

// Add node kind specific headers only for kinds that have data
for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
{
if (!haveNodeKindEntries[nodeKind])
continue;
const char* nodeKindName = mapNodeKind((NodeKind)nodeKind);
VStringBuffer prefix("%c%s ", toupper(*nodeKindName), nodeKindName + 1);
appendNodeKindHeaders(line, prefix);
}

appendNodeKindHeaders(line);
outputLine(line);

for (Cache::value_type& entry : stats)
Expand Down Expand Up @@ -749,7 +749,6 @@ class CTraceCollector : public CSummaryCollector

// Create aggregate statistics across all node kinds
NodeKindStats aggregateStats = {};
std::unordered_map<IndexHashKey, uint32_t, IndexHashKeyHash> allNodeMemory;

for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
aggregateStats.addStats(traceStats.kinds[nodeKind]);
Expand All @@ -770,7 +769,115 @@ class CTraceCollector : public CSummaryCollector
}
}

void summarizeByService()
{
// Extended TraceStats with trace counter for service summarization
class ServiceStats : public TraceStats
{
public:
uint32_t traceCount{0}; // Count of traces represented in this service
NodeKindStats totals;

ServiceStats() = default;
void addStats(const TraceStats& other)
{
for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
{
const NodeKindStats& nodeKindStats = other.kinds[nodeKind];
kinds[nodeKind].addStats(nodeKindStats);
totals.addStats(nodeKindStats);
}
}
};

// Aggregate trace stats by service name
std::unordered_map<std::string, ServiceStats> serviceStats;

for (Cache::value_type& entry : stats)
{
const TraceHashKey& traceKey = entry.first;
TraceStats& traceStats = entry.second;

// Find service name for this trace ID, or use empty string if not found
const char* serviceName = operation.queryMetaInfoState().queryServiceName(traceKey.traceId.str());
if (!serviceName)
serviceName = "";
ServiceStats& serviceAggregateStats = serviceStats[serviceName];

// Aggregate the stats
serviceAggregateStats.addStats(traceStats);

// Increment trace count
serviceAggregateStats.traceCount++;

// Merge unique files
serviceAggregateStats.uniqueFileIds.insert(traceStats.uniqueFileIds.begin(), traceStats.uniqueFileIds.end());

// Update timestamp ranges
if (traceStats.firstTimestamp != UINT64_MAX)
serviceAggregateStats.firstTimestamp = std::min(serviceAggregateStats.firstTimestamp, traceStats.firstTimestamp);
if (traceStats.lastTimestamp > 0)
serviceAggregateStats.lastTimestamp = std::max(serviceAggregateStats.lastTimestamp, traceStats.lastTimestamp);
}

// Output aggregated service stats
StringBuffer line;
appendCSVColumns(line, "Service Name", "Trace Count", "First Timestamp", "Last Timestamp", "Unique Files");
appendNodeKindHeaders(line);
outputLine(line);

for (auto& serviceEntry : serviceStats)
{
const std::string& serviceName = serviceEntry.first;
ServiceStats& serviceStatsEntry = serviceEntry.second;

// Format timestamps using CDateTime (empty if no valid timestamps were found)
StringBuffer firstTsStr, lastTsStr;
if (serviceStatsEntry.firstTimestamp != UINT64_MAX)
{
CDateTime firstDt;
firstDt.setTimeStampNs(serviceStatsEntry.firstTimestamp);
firstDt.getString(firstTsStr);
}
if (serviceStatsEntry.lastTimestamp > 0)
{
CDateTime lastDt;
lastDt.setTimeStampNs(serviceStatsEntry.lastTimestamp);
lastDt.getString(lastTsStr);
}

const char* displayServiceName = serviceName.empty() ? "<no service>" : serviceName.c_str();
appendCSVColumns(line, displayServiceName, serviceStatsEntry.traceCount, firstTsStr.str(), lastTsStr.str(), serviceStatsEntry.uniqueFileIds.size());
appendNodeKindData(line, serviceStatsEntry.totals);
for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
{
if (!haveNodeKindEntries[nodeKind])
continue;
const NodeKindStats& nodeStats = serviceStatsEntry.kinds[nodeKind];
appendNodeKindData(line, nodeStats);
}
outputLine(line);
}
}

protected:

void appendNodeKindHeaders(StringBuffer& line)
{
// Add aggregate statistics headers
appendNodeKindHeaders(line, "Total ");

// Add node kind specific headers only for kinds that have data
for (unsigned nodeKind = 0; nodeKind < NumNodeKinds; nodeKind++)
{
if (!haveNodeKindEntries[nodeKind])
continue;
const char* nodeKindName = mapNodeKind((NodeKind)nodeKind);
VStringBuffer prefix("%c%s ", toupper(*nodeKindName), nodeKindName + 1);
appendNodeKindHeaders(line, prefix);
}
}

void appendNodeKindHeaders(StringBuffer& line, const char* prefix)
{
appendCSVEventsHeaders(line, prefix);
Expand Down Expand Up @@ -807,6 +914,8 @@ class CTraceCollector : public CSummaryCollector
protected:
using Cache = std::unordered_map<TraceHashKey, TraceStats, TraceHashKeyHash>;
Cache stats;
bool haveNodeKindEntries[NumNodeKinds] = {false,};

};

bool CIndexFileSummary::doOp()
Expand All @@ -820,6 +929,7 @@ bool CIndexFileSummary::doOp()
collector.setown(new CNodeCollector(*this, summarization, out));
break;
case IndexSummarization::byTrace:
case IndexSummarization::byService:
collector.setown(new CTraceCollector(*this, summarization, out));
break;
default:
Expand Down
1 change: 1 addition & 0 deletions common/eventconsumption/eventindexsummarize.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ enum class IndexSummarization
byNodeKind,
byNode,
byTrace,
byService,
};

class event_decl CIndexFileSummary : public CEventConsumingOp
Expand Down
17 changes: 13 additions & 4 deletions docs/EN_US/ECLLanguageReference/ECLR_mods/BltInFunc-TOJSON.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
<sect1 id="TOJSON">
<title>TOJSON</title>

<para><emphasis role="bold">TOJSON<indexterm>
<para><emphasis role="bold">TOJSON(</emphasis><emphasis> record
</emphasis><emphasis role="bold">[,ALL])</emphasis><indexterm>
<primary>TOJSON</primary>
</indexterm><indexterm>
<primary>TOJSON function</primary>
</indexterm>(</emphasis><emphasis> record </emphasis><emphasis
role="bold">)</emphasis></para>
</indexterm></para>

<para><informaltable colsep="1" frame="all" rowsep="1">
<tgroup cols="2">
<colspec colwidth="77.95pt" />
<colspec colwidth="77.95pt"/>

<tbody>
<row>
Expand All @@ -22,6 +22,15 @@
<entry>The row (record) of data to convert to JSON format.</entry>
</row>

<row>
<entry><emphasis role="bold">ALL</emphasis></entry>

<entry>Specifies fields with empty strings are included in the
output. Without the ALL flag, <emphasis
role="bold">TOJSON</emphasis> omits fields that contain only
whitespace or are empty after trimming.</entry>
</row>

<row>
<entry>Return:</entry>

Expand Down
19 changes: 13 additions & 6 deletions docs/EN_US/ECLLanguageReference/ECLR_mods/BltInFunc-TOXML.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
<sect1 id="TOXML">
<title>TOXML</title>

<para><emphasis role="bold">TOXML<indexterm>
<para><emphasis role="bold">TOXML(</emphasis><emphasis> record
</emphasis><emphasis role="bold">[,ALL])</emphasis><indexterm>
<primary>TOXML</primary>
</indexterm><indexterm>
<primary>TOXML function</primary>
</indexterm>(</emphasis><emphasis> record </emphasis><emphasis
role="bold">)</emphasis></para>
</indexterm></para>

<para><informaltable colsep="1" frame="all" rowsep="1">
<tgroup cols="2">
<colspec colwidth="77.95pt" />
<colspec colwidth="77.95pt"/>

<tbody>
<row>
Expand All @@ -23,6 +23,15 @@
format.</entry>
</row>

<row>
<entry><emphasis role="bold">ALL</emphasis></entry>

<entry>Specifies fields with empty strings are included in the
output. Without the ALL flag, <emphasis
role="bold">TOXML</emphasis> omits fields that contain only
whitespace or are empty after trimming.</entry>
</row>

<row>
<entry>Return:</entry>

Expand All @@ -32,8 +41,6 @@
</tgroup>
</informaltable></para>

<para></para>

<para>The <emphasis role="bold">TOXML </emphasis>function returns a single
UTF-8 string with the data in the <emphasis>record</emphasis> re-formatted
as XML. If the RECORD structure of the <emphasis>record</emphasis> has
Expand Down
8 changes: 4 additions & 4 deletions docs/common/Version.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
<chapterinfo>
<date id="DateVer">DEVELOPER NON-GENERATED VERSION</date>

<releaseinfo id="FooterInfo">© 2025 HPCC
<releaseinfo id="FooterInfo">© 2026 HPCC
Systems<superscript>®</superscript>. All rights reserved</releaseinfo>

<copyright id="Copyright">
<year>2025 HPCC Systems<superscript>®</superscript>. All rights
<year>2026 HPCC Systems<superscript>®</superscript>. All rights
reserved</year>
</copyright>
</chapterinfo>
Expand All @@ -23,9 +23,9 @@
serve one purpose and that is to store the chapterinfo and other information
used by several document components.</para>

<para id="CHMVer">2025 Version ${DOC_VERSION}</para>
<para id="CHMVer">2026 Version ${DOC_VERSION}</para>

<remark id="YrVer">2025</remark>
<remark id="YrVer">2026</remark>

<para>The following line is the code to be put into the document you wish
to include the above version info in:</para>
Expand Down
10 changes: 5 additions & 5 deletions docs/common/Version.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter>
<chapterinfo>
<date id="DateVer">2025 Version ${DOC_VERSION}</date>
<date id="DateVer">2026 Version ${DOC_VERSION}</date>

<releaseinfo id="FooterInfo">© 2025 HPCC
<releaseinfo id="FooterInfo">© 2026 HPCC
Systems<superscript>®</superscript>. All rights reserved</releaseinfo>

<copyright id="Copyright">
<year>2025 HPCC Systems<superscript>®</superscript>. All rights
<year>2026 HPCC Systems<superscript>®</superscript>. All rights
reserved</year>
</copyright>
</chapterinfo>
Expand All @@ -24,9 +24,9 @@
serve one purpose and that is to store the chapterinfo the above sections
that are being used by several other documents.</para>

<para id="CHMVer">2025 Version ${DOC_VERSION}</para>
<para id="CHMVer">2026 Version ${DOC_VERSION}</para>

<remark id="YrVer">2025</remark>
<remark id="YrVer">2026</remark>

<para>The following line is the code to be put into the document you wish
to include the above version info in:</para>
Expand Down
Loading
Loading