@camstack/system 1.2.18 → 1.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -50,6 +50,12 @@ function formatLokiLine(entry) {
|
|
|
50
50
|
* Group a batch into one stream per distinct label set, and sort each stream's
|
|
51
51
|
* values by timestamp.
|
|
52
52
|
*
|
|
53
|
+
* The `node` label is resolved PER ENTRY from `tags.agentId` (the emitting
|
|
54
|
+
* agent, falling back to `labels.node` when absent), so a batch that mixes
|
|
55
|
+
* hub-local lines with forwarded agent lines fans out into one stream per
|
|
56
|
+
* origin node — the whole point of shipping the cluster's logs through the hub
|
|
57
|
+
* without flattening every line onto `node="hub"`.
|
|
58
|
+
*
|
|
53
59
|
* The sort is not cosmetic. Loki historically rejected a stream whose entries
|
|
54
60
|
* were not in ascending time order, and even with out-of-order ingestion enabled
|
|
55
61
|
* it is bounded by a window. Entries reach a destination from several concurrent
|
|
@@ -58,21 +64,26 @@ function formatLokiLine(entry) {
|
|
|
58
64
|
*/
|
|
59
65
|
function buildPushBody(entries, labels) {
|
|
60
66
|
if (entries.length === 0) return null;
|
|
61
|
-
const base = {
|
|
62
|
-
job: labels.job,
|
|
63
|
-
node: labels.node
|
|
64
|
-
};
|
|
67
|
+
const base = { job: labels.job };
|
|
65
68
|
for (const [key, value] of Object.entries(labels.extra ?? {})) base[key] = value;
|
|
66
|
-
const
|
|
69
|
+
const byStream = /* @__PURE__ */ new Map();
|
|
67
70
|
for (const entry of entries) {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
const agentId = entry.tags?.agentId;
|
|
72
|
+
const node = typeof agentId === "string" && agentId.length > 0 ? agentId : labels.node;
|
|
73
|
+
const key = `${node}\u0000${entry.level}`;
|
|
74
|
+
const bucket = byStream.get(key) ?? {
|
|
75
|
+
node,
|
|
76
|
+
level: entry.level,
|
|
77
|
+
values: []
|
|
78
|
+
};
|
|
79
|
+
bucket.values.push([toUnixNano(entry.timestamp), formatLokiLine(entry)]);
|
|
80
|
+
byStream.set(key, bucket);
|
|
71
81
|
}
|
|
72
82
|
const streams = [];
|
|
73
|
-
for (const
|
|
83
|
+
for (const { node, level, values } of byStream.values()) streams.push({
|
|
74
84
|
stream: {
|
|
75
85
|
...base,
|
|
86
|
+
node,
|
|
76
87
|
level
|
|
77
88
|
},
|
|
78
89
|
values: values.toSorted((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)
|
|
@@ -45,6 +45,12 @@ function formatLokiLine(entry) {
|
|
|
45
45
|
* Group a batch into one stream per distinct label set, and sort each stream's
|
|
46
46
|
* values by timestamp.
|
|
47
47
|
*
|
|
48
|
+
* The `node` label is resolved PER ENTRY from `tags.agentId` (the emitting
|
|
49
|
+
* agent, falling back to `labels.node` when absent), so a batch that mixes
|
|
50
|
+
* hub-local lines with forwarded agent lines fans out into one stream per
|
|
51
|
+
* origin node — the whole point of shipping the cluster's logs through the hub
|
|
52
|
+
* without flattening every line onto `node="hub"`.
|
|
53
|
+
*
|
|
48
54
|
* The sort is not cosmetic. Loki historically rejected a stream whose entries
|
|
49
55
|
* were not in ascending time order, and even with out-of-order ingestion enabled
|
|
50
56
|
* it is bounded by a window. Entries reach a destination from several concurrent
|
|
@@ -53,21 +59,26 @@ function formatLokiLine(entry) {
|
|
|
53
59
|
*/
|
|
54
60
|
function buildPushBody(entries, labels) {
|
|
55
61
|
if (entries.length === 0) return null;
|
|
56
|
-
const base = {
|
|
57
|
-
job: labels.job,
|
|
58
|
-
node: labels.node
|
|
59
|
-
};
|
|
62
|
+
const base = { job: labels.job };
|
|
60
63
|
for (const [key, value] of Object.entries(labels.extra ?? {})) base[key] = value;
|
|
61
|
-
const
|
|
64
|
+
const byStream = /* @__PURE__ */ new Map();
|
|
62
65
|
for (const entry of entries) {
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
const agentId = entry.tags?.agentId;
|
|
67
|
+
const node = typeof agentId === "string" && agentId.length > 0 ? agentId : labels.node;
|
|
68
|
+
const key = `${node}\u0000${entry.level}`;
|
|
69
|
+
const bucket = byStream.get(key) ?? {
|
|
70
|
+
node,
|
|
71
|
+
level: entry.level,
|
|
72
|
+
values: []
|
|
73
|
+
};
|
|
74
|
+
bucket.values.push([toUnixNano(entry.timestamp), formatLokiLine(entry)]);
|
|
75
|
+
byStream.set(key, bucket);
|
|
66
76
|
}
|
|
67
77
|
const streams = [];
|
|
68
|
-
for (const
|
|
78
|
+
for (const { node, level, values } of byStream.values()) streams.push({
|
|
69
79
|
stream: {
|
|
70
80
|
...base,
|
|
81
|
+
node,
|
|
71
82
|
level
|
|
72
83
|
},
|
|
73
84
|
values: values.toSorted((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)
|
|
@@ -11,7 +11,12 @@ export interface LokiPushBody {
|
|
|
11
11
|
export interface LokiLabelInput {
|
|
12
12
|
/** Fixed `job` label. Bounded by configuration, never per-entry. */
|
|
13
13
|
readonly job: string;
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Fallback node id for entries that carry no `tags.agentId` origin — the
|
|
16
|
+
* id of the node running this destination. The per-entry origin
|
|
17
|
+
* (`tags.agentId`) is preferred so forwarded agent logs are attributed to
|
|
18
|
+
* their source node rather than the hub that ships them.
|
|
19
|
+
*/
|
|
15
20
|
readonly node: string;
|
|
16
21
|
/** Extra static labels from configuration. Values must be low-cardinality. */
|
|
17
22
|
readonly extra?: Readonly<Record<string, string>>;
|
|
@@ -37,6 +42,12 @@ export declare function formatLokiLine(entry: LogEntry): string;
|
|
|
37
42
|
* Group a batch into one stream per distinct label set, and sort each stream's
|
|
38
43
|
* values by timestamp.
|
|
39
44
|
*
|
|
45
|
+
* The `node` label is resolved PER ENTRY from `tags.agentId` (the emitting
|
|
46
|
+
* agent, falling back to `labels.node` when absent), so a batch that mixes
|
|
47
|
+
* hub-local lines with forwarded agent lines fans out into one stream per
|
|
48
|
+
* origin node — the whole point of shipping the cluster's logs through the hub
|
|
49
|
+
* without flattening every line onto `node="hub"`.
|
|
50
|
+
*
|
|
40
51
|
* The sort is not cosmetic. Loki historically rejected a stream whose entries
|
|
41
52
|
* were not in ascending time order, and even with out-of-order ingestion enabled
|
|
42
53
|
* it is bounded by a window. Entries reach a destination from several concurrent
|