hobots 2.2.0 → 2.3.0
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.
- package/README.md +14 -2
- package/dist/index.cjs +196 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -3
- package/dist/index.d.ts +40 -3
- package/dist/index.js +196 -42
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +57 -1
- package/dist/protocol.d.ts +57 -1
- package/docs/api-reference.md +3 -0
- package/docs/protocol.md +22 -0
- package/docs/telemetry.md +11 -0
- package/package.json +5 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Breadcrumb, EventUser, EventType, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress, RunAttachmentContentType } from './protocol.cjs';
|
|
2
|
-
export { BreadcrumbLevel, Envelope, EnvelopeItem, ExceptionData, InstanceInfo, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.cjs';
|
|
1
|
+
import { Breadcrumb, EventUser, EventType, MachineSnapshot, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress, RunAttachmentContentType } from './protocol.cjs';
|
|
2
|
+
export { BreadcrumbLevel, Envelope, EnvelopeItem, ExceptionData, InstanceInfo, MachineProcess, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.cjs';
|
|
3
3
|
|
|
4
4
|
/** Aceita tudo que o handler pode ter em mãos: bytes ou um caminho de arquivo (só Node). */
|
|
5
5
|
type AttachInput = Uint8Array | ArrayBuffer | string;
|
|
@@ -47,15 +47,46 @@ interface Logger {
|
|
|
47
47
|
error: (...args: unknown[]) => void;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Coleta CPU/memória/disco da máquina e do processo — só em Node (no browser é no-op).
|
|
52
|
+
*
|
|
53
|
+
* Os módulos `node:*` entram por import dinâmico (o bundle é `platform: neutral`), e
|
|
54
|
+
* `sample()` é síncrono e barato: `os.cpus()` + `statfsSync` + `process.memoryUsage()`.
|
|
55
|
+
* Nunca lança — telemetria de recurso não pode derrubar o bot nem o capture de um erro.
|
|
56
|
+
*/
|
|
57
|
+
declare class MachineSampler {
|
|
58
|
+
private readonly instanceId;
|
|
59
|
+
private readonly logger;
|
|
60
|
+
private os;
|
|
61
|
+
private fs;
|
|
62
|
+
private ready;
|
|
63
|
+
private prevCpu;
|
|
64
|
+
private prevProcessCpu;
|
|
65
|
+
private prevAt;
|
|
66
|
+
private lastCpuPercent;
|
|
67
|
+
private lastProcessCpuPercent;
|
|
68
|
+
constructor(instanceId: string, logger: Logger);
|
|
69
|
+
/** Carrega os módulos e tira a leitura-base de CPU (a primeira amostra ainda sai com cpu_percent null). */
|
|
70
|
+
start(): Promise<void>;
|
|
71
|
+
sample(): MachineSnapshot | undefined;
|
|
72
|
+
private cpuTotals;
|
|
73
|
+
/** Média desde a leitura anterior; leituras com menos de 1s de janela reutilizam a última média. */
|
|
74
|
+
private updateCpu;
|
|
75
|
+
private diskUsage;
|
|
76
|
+
}
|
|
77
|
+
|
|
50
78
|
declare class Client {
|
|
51
79
|
#private;
|
|
52
80
|
readonly logger: Logger;
|
|
53
81
|
/** Eco de lifecycle (Transaction.emit) e afins só imprimem no terminal com debug ligado. */
|
|
54
82
|
readonly debug: boolean;
|
|
55
83
|
private readonly transport;
|
|
84
|
+
private machine;
|
|
56
85
|
constructor(options: InitOptions, config?: ResolvedConfig);
|
|
57
86
|
get options(): InitOptions;
|
|
58
87
|
get config(): ResolvedConfig;
|
|
88
|
+
/** Sampler de recursos da máquina — cada evento passa a levar `contexts.machine`. */
|
|
89
|
+
attachMachineSampler(sampler: MachineSampler | undefined): void;
|
|
59
90
|
/** Serialização explícita nunca inclui o secret. */
|
|
60
91
|
toJSON(): Record<string, unknown>;
|
|
61
92
|
captureException(error: unknown, hint: CaptureHint | undefined, scope: Scope): string;
|
|
@@ -270,6 +301,12 @@ interface InitOptions {
|
|
|
270
301
|
maxBreadcrumbs?: number;
|
|
271
302
|
/** Registra handlers globais de erro (uncaughtException etc.). Default: true */
|
|
272
303
|
captureUnhandled?: boolean;
|
|
304
|
+
/**
|
|
305
|
+
* Coleta CPU, memória, disco e recursos do processo: vai em cada heartbeat/poll (série da
|
|
306
|
+
* instância no app) e como `contexts.machine` em cada evento (como estava a máquina na hora
|
|
307
|
+
* do erro). Só em Node; sem custo de timer — pega carona no heartbeat. Default: true
|
|
308
|
+
*/
|
|
309
|
+
machineMetrics?: boolean;
|
|
273
310
|
/** Habilita heartbeat periódico (true usa defaults) */
|
|
274
311
|
heartbeat?: boolean | HeartbeatOptions;
|
|
275
312
|
/**
|
|
@@ -356,4 +393,4 @@ declare function close(timeoutMs?: number): Promise<boolean>;
|
|
|
356
393
|
|
|
357
394
|
declare function scrubSecrets(text: string): string;
|
|
358
395
|
|
|
359
|
-
export { type AttachInput, Breadcrumb, type BreadcrumbInput, type CaptureAttachment, type CaptureHint, Client, DEFAULT_BASE_URL, EventPayload, EventType, EventUser, type HeartbeatOptions, HeartbeatPayload, type InitOptions, RunAttachmentContentType, type RunContext, type RunItemOptions, RunItemStatus, type RunItems, RunLogLevel, RunProgress, type RunProgressReporter, Scope, Step, StepData, type TaskHandler, type TasksOptions, Transaction, TransactionPayload, addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, register, scrubSecrets, setContext, setExtra, setTag, setTags, setUser, start, startTransaction, stop, withScope };
|
|
396
|
+
export { type AttachInput, Breadcrumb, type BreadcrumbInput, type CaptureAttachment, type CaptureHint, Client, DEFAULT_BASE_URL, EventPayload, EventType, EventUser, type HeartbeatOptions, HeartbeatPayload, type InitOptions, MachineSnapshot, RunAttachmentContentType, type RunContext, type RunItemOptions, RunItemStatus, type RunItems, RunLogLevel, RunProgress, type RunProgressReporter, Scope, Step, StepData, type TaskHandler, type TasksOptions, Transaction, TransactionPayload, addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, register, scrubSecrets, setContext, setExtra, setTag, setTags, setUser, start, startTransaction, stop, withScope };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Breadcrumb, EventUser, EventType, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress, RunAttachmentContentType } from './protocol.js';
|
|
2
|
-
export { BreadcrumbLevel, Envelope, EnvelopeItem, ExceptionData, InstanceInfo, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.js';
|
|
1
|
+
import { Breadcrumb, EventUser, EventType, MachineSnapshot, EventPayload, TransactionPayload, HeartbeatPayload, StepStatus, RunLogLevel, StepData, RunItemStatus, RunProgress, RunAttachmentContentType } from './protocol.js';
|
|
2
|
+
export { BreadcrumbLevel, Envelope, EnvelopeItem, ExceptionData, InstanceInfo, MachineProcess, PollRequest, PollResponse, RunAttachmentConfirmRequest, RunAttachmentConfirmResponse, RunAttachmentCreateRequest, RunAttachmentCreateResponse, RunDescriptor, RunError, RunItemInput, RunItemsCreateRequest, RunItemsCreateResponse, RunLogEntry, RunStatus, RunUpdateRequest, RunUpdateResponse, RunUpdateStatus, StackFrame } from './protocol.js';
|
|
3
3
|
|
|
4
4
|
/** Aceita tudo que o handler pode ter em mãos: bytes ou um caminho de arquivo (só Node). */
|
|
5
5
|
type AttachInput = Uint8Array | ArrayBuffer | string;
|
|
@@ -47,15 +47,46 @@ interface Logger {
|
|
|
47
47
|
error: (...args: unknown[]) => void;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Coleta CPU/memória/disco da máquina e do processo — só em Node (no browser é no-op).
|
|
52
|
+
*
|
|
53
|
+
* Os módulos `node:*` entram por import dinâmico (o bundle é `platform: neutral`), e
|
|
54
|
+
* `sample()` é síncrono e barato: `os.cpus()` + `statfsSync` + `process.memoryUsage()`.
|
|
55
|
+
* Nunca lança — telemetria de recurso não pode derrubar o bot nem o capture de um erro.
|
|
56
|
+
*/
|
|
57
|
+
declare class MachineSampler {
|
|
58
|
+
private readonly instanceId;
|
|
59
|
+
private readonly logger;
|
|
60
|
+
private os;
|
|
61
|
+
private fs;
|
|
62
|
+
private ready;
|
|
63
|
+
private prevCpu;
|
|
64
|
+
private prevProcessCpu;
|
|
65
|
+
private prevAt;
|
|
66
|
+
private lastCpuPercent;
|
|
67
|
+
private lastProcessCpuPercent;
|
|
68
|
+
constructor(instanceId: string, logger: Logger);
|
|
69
|
+
/** Carrega os módulos e tira a leitura-base de CPU (a primeira amostra ainda sai com cpu_percent null). */
|
|
70
|
+
start(): Promise<void>;
|
|
71
|
+
sample(): MachineSnapshot | undefined;
|
|
72
|
+
private cpuTotals;
|
|
73
|
+
/** Média desde a leitura anterior; leituras com menos de 1s de janela reutilizam a última média. */
|
|
74
|
+
private updateCpu;
|
|
75
|
+
private diskUsage;
|
|
76
|
+
}
|
|
77
|
+
|
|
50
78
|
declare class Client {
|
|
51
79
|
#private;
|
|
52
80
|
readonly logger: Logger;
|
|
53
81
|
/** Eco de lifecycle (Transaction.emit) e afins só imprimem no terminal com debug ligado. */
|
|
54
82
|
readonly debug: boolean;
|
|
55
83
|
private readonly transport;
|
|
84
|
+
private machine;
|
|
56
85
|
constructor(options: InitOptions, config?: ResolvedConfig);
|
|
57
86
|
get options(): InitOptions;
|
|
58
87
|
get config(): ResolvedConfig;
|
|
88
|
+
/** Sampler de recursos da máquina — cada evento passa a levar `contexts.machine`. */
|
|
89
|
+
attachMachineSampler(sampler: MachineSampler | undefined): void;
|
|
59
90
|
/** Serialização explícita nunca inclui o secret. */
|
|
60
91
|
toJSON(): Record<string, unknown>;
|
|
61
92
|
captureException(error: unknown, hint: CaptureHint | undefined, scope: Scope): string;
|
|
@@ -270,6 +301,12 @@ interface InitOptions {
|
|
|
270
301
|
maxBreadcrumbs?: number;
|
|
271
302
|
/** Registra handlers globais de erro (uncaughtException etc.). Default: true */
|
|
272
303
|
captureUnhandled?: boolean;
|
|
304
|
+
/**
|
|
305
|
+
* Coleta CPU, memória, disco e recursos do processo: vai em cada heartbeat/poll (série da
|
|
306
|
+
* instância no app) e como `contexts.machine` em cada evento (como estava a máquina na hora
|
|
307
|
+
* do erro). Só em Node; sem custo de timer — pega carona no heartbeat. Default: true
|
|
308
|
+
*/
|
|
309
|
+
machineMetrics?: boolean;
|
|
273
310
|
/** Habilita heartbeat periódico (true usa defaults) */
|
|
274
311
|
heartbeat?: boolean | HeartbeatOptions;
|
|
275
312
|
/**
|
|
@@ -356,4 +393,4 @@ declare function close(timeoutMs?: number): Promise<boolean>;
|
|
|
356
393
|
|
|
357
394
|
declare function scrubSecrets(text: string): string;
|
|
358
395
|
|
|
359
|
-
export { type AttachInput, Breadcrumb, type BreadcrumbInput, type CaptureAttachment, type CaptureHint, Client, DEFAULT_BASE_URL, EventPayload, EventType, EventUser, type HeartbeatOptions, HeartbeatPayload, type InitOptions, RunAttachmentContentType, type RunContext, type RunItemOptions, RunItemStatus, type RunItems, RunLogLevel, RunProgress, type RunProgressReporter, Scope, Step, StepData, type TaskHandler, type TasksOptions, Transaction, TransactionPayload, addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, register, scrubSecrets, setContext, setExtra, setTag, setTags, setUser, start, startTransaction, stop, withScope };
|
|
396
|
+
export { type AttachInput, Breadcrumb, type BreadcrumbInput, type CaptureAttachment, type CaptureHint, Client, DEFAULT_BASE_URL, EventPayload, EventType, EventUser, type HeartbeatOptions, HeartbeatPayload, type InitOptions, MachineSnapshot, RunAttachmentContentType, type RunContext, type RunItemOptions, RunItemStatus, type RunItems, RunLogLevel, RunProgress, type RunProgressReporter, Scope, Step, StepData, type TaskHandler, type TasksOptions, Transaction, TransactionPayload, addBreadcrumb, captureException, captureMessage, close, flush, getClient, init, register, scrubSecrets, setContext, setExtra, setTag, setTags, setUser, start, startTransaction, stop, withScope };
|
package/dist/index.js
CHANGED
|
@@ -103,7 +103,7 @@ function scrubSecrets(text) {
|
|
|
103
103
|
|
|
104
104
|
// src/utils.ts
|
|
105
105
|
var SDK_NAME = "hobots";
|
|
106
|
-
var SDK_VERSION = true ? "2.
|
|
106
|
+
var SDK_VERSION = true ? "2.3.0" : "0.0.0-dev";
|
|
107
107
|
function isNode() {
|
|
108
108
|
return typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
109
109
|
}
|
|
@@ -396,6 +396,7 @@ var Client = class {
|
|
|
396
396
|
#options;
|
|
397
397
|
#config;
|
|
398
398
|
transport;
|
|
399
|
+
machine;
|
|
399
400
|
constructor(options, config = resolveConfig(options)) {
|
|
400
401
|
this.#options = options;
|
|
401
402
|
this.debug = !!options.debug;
|
|
@@ -415,6 +416,10 @@ var Client = class {
|
|
|
415
416
|
get config() {
|
|
416
417
|
return this.#config;
|
|
417
418
|
}
|
|
419
|
+
/** Sampler de recursos da máquina — cada evento passa a levar `contexts.machine`. */
|
|
420
|
+
attachMachineSampler(sampler) {
|
|
421
|
+
this.machine = sampler;
|
|
422
|
+
}
|
|
418
423
|
/** Serialização explícita nunca inclui o secret. */
|
|
419
424
|
toJSON() {
|
|
420
425
|
return { sdk: SDK_NAME, version: SDK_VERSION, baseUrl: this.#config.baseUrl };
|
|
@@ -446,6 +451,8 @@ var Client = class {
|
|
|
446
451
|
prepareEvent(event, hint, scope2) {
|
|
447
452
|
const tags = { ...this.options.tags, ...scope2.tags, ...event.tags, ...hint?.tags };
|
|
448
453
|
const extra = { ...scope2.extra, ...event.extra, ...hint?.extra };
|
|
454
|
+
const sampled = this.machine?.sample();
|
|
455
|
+
const machine = sampled ? { machine: sampled } : {};
|
|
449
456
|
return {
|
|
450
457
|
...event,
|
|
451
458
|
type: hint?.type ?? event.type ?? scope2.type ?? "error",
|
|
@@ -454,7 +461,7 @@ var Client = class {
|
|
|
454
461
|
tags: Object.keys(tags).length > 0 ? tags : void 0,
|
|
455
462
|
extra: Object.keys(extra).length > 0 ? extra : void 0,
|
|
456
463
|
user: event.user ?? scope2.user,
|
|
457
|
-
contexts: { ...runtimeContexts(), ...scope2.contexts, ...event.contexts },
|
|
464
|
+
contexts: { ...runtimeContexts(), ...machine, ...scope2.contexts, ...event.contexts },
|
|
458
465
|
breadcrumbs: scope2.breadcrumbs.length > 0 ? [...scope2.breadcrumbs] : event.breadcrumbs,
|
|
459
466
|
fingerprint: hint?.fingerprint ?? event.fingerprint ?? scope2.fingerprint
|
|
460
467
|
};
|
|
@@ -489,8 +496,9 @@ var DEFAULT_INTERVAL_MS = 3e4;
|
|
|
489
496
|
var MIN_REPORTED_INTERVAL_MS = 1e3;
|
|
490
497
|
var MAX_REPORTED_INTERVAL_MS = 9e5;
|
|
491
498
|
var Heartbeat = class {
|
|
492
|
-
constructor(client2, options = {}, instanceId) {
|
|
499
|
+
constructor(client2, options = {}, instanceId, sampler) {
|
|
493
500
|
this.client = client2;
|
|
501
|
+
this.sampler = sampler;
|
|
494
502
|
this.intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
495
503
|
this.instanceId = instanceId;
|
|
496
504
|
this.name = options.name ?? instanceId;
|
|
@@ -529,7 +537,9 @@ var Heartbeat = class {
|
|
|
529
537
|
name: this.name,
|
|
530
538
|
interval_ms: Math.min(Math.max(this.intervalMs, MIN_REPORTED_INTERVAL_MS), MAX_REPORTED_INTERVAL_MS),
|
|
531
539
|
timestamp: nowISO(),
|
|
532
|
-
metadata: this.metadata
|
|
540
|
+
metadata: this.metadata,
|
|
541
|
+
// recursos da máquina a cada beat — a série da instância no app
|
|
542
|
+
machine: this.sampler?.sample()
|
|
533
543
|
});
|
|
534
544
|
}
|
|
535
545
|
};
|
|
@@ -560,50 +570,184 @@ function hasInstalledGlobalHandlers() {
|
|
|
560
570
|
return installed;
|
|
561
571
|
}
|
|
562
572
|
function installGlobalHandlers(client2, getScope) {
|
|
563
|
-
if (installed) return;
|
|
573
|
+
if (installed) return () => void 0;
|
|
564
574
|
installed = true;
|
|
565
575
|
if (isNode()) {
|
|
566
|
-
|
|
567
|
-
"uncaughtException",
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
})
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
);
|
|
586
|
-
|
|
587
|
-
"
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return;
|
|
576
|
+
const onUncaught = markInternal((error) => {
|
|
577
|
+
client2.captureException(error, { type: "fatal", tags: { mechanism: "uncaughtException" } }, getScope());
|
|
578
|
+
void client2.flush(2e3).then(async () => {
|
|
579
|
+
if (externalListenerCount("uncaughtException") === 0) {
|
|
580
|
+
await awaitEmergencyTasks(EMERGENCY_REPORT_CAP_MS);
|
|
581
|
+
console.error(error);
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
const onRejection = markInternal((reason) => {
|
|
587
|
+
client2.captureException(reason, { type: "error", tags: { mechanism: "unhandledRejection" } }, getScope());
|
|
588
|
+
void client2.flush(2e3);
|
|
589
|
+
});
|
|
590
|
+
const onBeforeExit = markInternal(() => {
|
|
591
|
+
void client2.flush(2e3);
|
|
592
|
+
});
|
|
593
|
+
process.on("uncaughtException", onUncaught);
|
|
594
|
+
process.on("unhandledRejection", onRejection);
|
|
595
|
+
process.on("beforeExit", onBeforeExit);
|
|
596
|
+
return () => {
|
|
597
|
+
process.off("uncaughtException", onUncaught);
|
|
598
|
+
process.off("unhandledRejection", onRejection);
|
|
599
|
+
process.off("beforeExit", onBeforeExit);
|
|
600
|
+
installed = false;
|
|
601
|
+
};
|
|
593
602
|
}
|
|
594
603
|
if (typeof window !== "undefined") {
|
|
595
|
-
|
|
604
|
+
const onError = (event) => {
|
|
596
605
|
client2.captureException(event.error ?? event.message, { tags: { mechanism: "onerror" } }, getScope());
|
|
597
|
-
}
|
|
598
|
-
|
|
606
|
+
};
|
|
607
|
+
const onRejection = (event) => {
|
|
599
608
|
client2.captureException(event.reason, { tags: { mechanism: "onunhandledrejection" } }, getScope());
|
|
600
|
-
}
|
|
601
|
-
|
|
609
|
+
};
|
|
610
|
+
const onPageHide = () => {
|
|
602
611
|
void client2.flush(1e3);
|
|
603
|
-
}
|
|
612
|
+
};
|
|
613
|
+
window.addEventListener("error", onError);
|
|
614
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
615
|
+
window.addEventListener("pagehide", onPageHide);
|
|
616
|
+
return () => {
|
|
617
|
+
window.removeEventListener("error", onError);
|
|
618
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
619
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
620
|
+
installed = false;
|
|
621
|
+
};
|
|
604
622
|
}
|
|
623
|
+
return () => {
|
|
624
|
+
installed = false;
|
|
625
|
+
};
|
|
605
626
|
}
|
|
606
627
|
|
|
628
|
+
// src/machine.ts
|
|
629
|
+
var MIN_CPU_WINDOW_MS = 1e3;
|
|
630
|
+
var round1 = (value) => Math.round(value * 10) / 10;
|
|
631
|
+
var clampPercent = (value) => Math.min(100, Math.max(0, value));
|
|
632
|
+
var MachineSampler = class {
|
|
633
|
+
constructor(instanceId, logger) {
|
|
634
|
+
this.instanceId = instanceId;
|
|
635
|
+
this.logger = logger;
|
|
636
|
+
}
|
|
637
|
+
os;
|
|
638
|
+
fs;
|
|
639
|
+
ready = false;
|
|
640
|
+
prevCpu;
|
|
641
|
+
prevProcessCpu;
|
|
642
|
+
prevAt = 0;
|
|
643
|
+
lastCpuPercent = null;
|
|
644
|
+
lastProcessCpuPercent = null;
|
|
645
|
+
/** Carrega os módulos e tira a leitura-base de CPU (a primeira amostra ainda sai com cpu_percent null). */
|
|
646
|
+
async start() {
|
|
647
|
+
if (!isNode()) return;
|
|
648
|
+
try {
|
|
649
|
+
this.os = await import("os");
|
|
650
|
+
this.fs = await import("fs");
|
|
651
|
+
this.prevCpu = this.cpuTotals();
|
|
652
|
+
this.prevProcessCpu = process.cpuUsage();
|
|
653
|
+
this.prevAt = Date.now();
|
|
654
|
+
this.ready = true;
|
|
655
|
+
} catch (error) {
|
|
656
|
+
this.logger.debug("m\xE9tricas de m\xE1quina indispon\xEDveis neste runtime", error);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
sample() {
|
|
660
|
+
if (!this.ready || !this.os) return void 0;
|
|
661
|
+
try {
|
|
662
|
+
const os = this.os;
|
|
663
|
+
const cpuCount = Math.max(1, os.cpus().length);
|
|
664
|
+
this.updateCpu(cpuCount);
|
|
665
|
+
const snapshot = {
|
|
666
|
+
sampled_at: nowISO(),
|
|
667
|
+
instance_id: this.instanceId,
|
|
668
|
+
hostname: os.hostname(),
|
|
669
|
+
os: process.platform,
|
|
670
|
+
arch: process.arch,
|
|
671
|
+
cpu_count: cpuCount,
|
|
672
|
+
cpu_percent: this.lastCpuPercent,
|
|
673
|
+
process: {
|
|
674
|
+
pid: process.pid,
|
|
675
|
+
uptime_s: Math.round(process.uptime()),
|
|
676
|
+
cpu_percent: this.lastProcessCpuPercent
|
|
677
|
+
},
|
|
678
|
+
source: "stdlib"
|
|
679
|
+
};
|
|
680
|
+
if (process.platform !== "win32") {
|
|
681
|
+
const load = os.loadavg()[0];
|
|
682
|
+
if (typeof load === "number" && Number.isFinite(load)) snapshot.load_1 = round1(load);
|
|
683
|
+
}
|
|
684
|
+
const memTotal = os.totalmem();
|
|
685
|
+
const memFree = os.freemem();
|
|
686
|
+
if (memTotal > 0) {
|
|
687
|
+
snapshot.mem_total_bytes = memTotal;
|
|
688
|
+
snapshot.mem_available_bytes = memFree;
|
|
689
|
+
snapshot.mem_percent = round1(clampPercent((memTotal - memFree) / memTotal * 100));
|
|
690
|
+
}
|
|
691
|
+
const disk = this.diskUsage();
|
|
692
|
+
if (disk) Object.assign(snapshot, disk);
|
|
693
|
+
const mem = process.memoryUsage();
|
|
694
|
+
snapshot.process.rss_bytes = mem.rss;
|
|
695
|
+
snapshot.process.heap_used_bytes = mem.heapUsed;
|
|
696
|
+
return snapshot;
|
|
697
|
+
} catch (error) {
|
|
698
|
+
this.logger.debug("falha ao coletar m\xE9tricas de m\xE1quina", error);
|
|
699
|
+
return void 0;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
cpuTotals() {
|
|
703
|
+
let busy = 0;
|
|
704
|
+
let total = 0;
|
|
705
|
+
for (const cpu of this.os.cpus()) {
|
|
706
|
+
const t = cpu.times;
|
|
707
|
+
const cpuBusy = t.user + t.nice + t.sys + t.irq;
|
|
708
|
+
busy += cpuBusy;
|
|
709
|
+
total += cpuBusy + t.idle;
|
|
710
|
+
}
|
|
711
|
+
return { busy, total };
|
|
712
|
+
}
|
|
713
|
+
/** Média desde a leitura anterior; leituras com menos de 1s de janela reutilizam a última média. */
|
|
714
|
+
updateCpu(cpuCount) {
|
|
715
|
+
const now = Date.now();
|
|
716
|
+
const elapsedMs = now - this.prevAt;
|
|
717
|
+
if (elapsedMs < MIN_CPU_WINDOW_MS) return;
|
|
718
|
+
const cpu = this.cpuTotals();
|
|
719
|
+
if (this.prevCpu) {
|
|
720
|
+
const total = cpu.total - this.prevCpu.total;
|
|
721
|
+
if (total > 0) this.lastCpuPercent = round1(clampPercent((cpu.busy - this.prevCpu.busy) / total * 100));
|
|
722
|
+
}
|
|
723
|
+
this.prevCpu = cpu;
|
|
724
|
+
const usage = process.cpuUsage(this.prevProcessCpu);
|
|
725
|
+
const usedMicros = usage.user + usage.system;
|
|
726
|
+
this.lastProcessCpuPercent = round1(clampPercent(usedMicros / (elapsedMs * 1e3 * cpuCount) * 100));
|
|
727
|
+
this.prevProcessCpu = process.cpuUsage();
|
|
728
|
+
this.prevAt = now;
|
|
729
|
+
}
|
|
730
|
+
diskUsage() {
|
|
731
|
+
const statfs = this.fs?.statfsSync;
|
|
732
|
+
if (typeof statfs !== "function") return void 0;
|
|
733
|
+
try {
|
|
734
|
+
const path = process.cwd();
|
|
735
|
+
const stat = statfs(path);
|
|
736
|
+
const total = stat.bsize * stat.blocks;
|
|
737
|
+
const free = stat.bsize * stat.bavail;
|
|
738
|
+
if (total <= 0) return void 0;
|
|
739
|
+
return {
|
|
740
|
+
disk_path: path,
|
|
741
|
+
disk_total_bytes: total,
|
|
742
|
+
disk_free_bytes: free,
|
|
743
|
+
disk_percent: round1(clampPercent((total - free) / total * 100))
|
|
744
|
+
};
|
|
745
|
+
} catch {
|
|
746
|
+
return void 0;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
|
|
607
751
|
// src/scope.ts
|
|
608
752
|
var Scope = class _Scope {
|
|
609
753
|
tags = {};
|
|
@@ -1922,7 +2066,10 @@ var TaskAgent = class {
|
|
|
1922
2066
|
// o servidor calcula a tolerância de offline (3×) a partir disto em
|
|
1923
2067
|
// agentes poll-only — sem o campo, um poll lento flaparia offline/online.
|
|
1924
2068
|
// Clampado à faixa aceita pelo schema do poll (1s..15min).
|
|
1925
|
-
poll_interval_ms: Math.min(Math.max(this.pollIntervalMs, 1e3), 9e5)
|
|
2069
|
+
poll_interval_ms: Math.min(Math.max(this.pollIntervalMs, 1e3), 9e5),
|
|
2070
|
+
// recursos da máquina — alimenta a série da instância em agentes sem heartbeat
|
|
2071
|
+
// (o servidor grava no máx. 1 ponto/30s por instância)
|
|
2072
|
+
machine: this.options.machineSampler?.sample()
|
|
1926
2073
|
},
|
|
1927
2074
|
max: Math.min(MAX_CONCURRENCY, this.concurrency - this.activeRuns.size),
|
|
1928
2075
|
tasks: [...this.registry.keys()]
|
|
@@ -2070,7 +2217,9 @@ var DEFAULT_MAX_BREADCRUMBS = 100;
|
|
|
2070
2217
|
var client;
|
|
2071
2218
|
var scope = new Scope();
|
|
2072
2219
|
var heartbeat;
|
|
2220
|
+
var machineSampler;
|
|
2073
2221
|
var agent;
|
|
2222
|
+
var uninstallGlobalHandlers;
|
|
2074
2223
|
var registry = /* @__PURE__ */ new Map();
|
|
2075
2224
|
var warnedTasksDisabled = false;
|
|
2076
2225
|
function init(options) {
|
|
@@ -2083,21 +2232,26 @@ function init(options) {
|
|
|
2083
2232
|
void agent.stop(0);
|
|
2084
2233
|
agent = void 0;
|
|
2085
2234
|
}
|
|
2235
|
+
uninstallGlobalHandlers?.();
|
|
2236
|
+
uninstallGlobalHandlers = void 0;
|
|
2086
2237
|
warnedTasksDisabled = false;
|
|
2087
2238
|
const config = resolveConfig(options);
|
|
2088
2239
|
client = new Client(options, config);
|
|
2240
|
+
machineSampler = options.machineMetrics === false ? void 0 : new MachineSampler(options.instanceId, client.logger);
|
|
2241
|
+
void machineSampler?.start();
|
|
2242
|
+
client.attachMachineSampler(machineSampler);
|
|
2089
2243
|
if (options.captureUnhandled !== false) {
|
|
2090
|
-
installGlobalHandlers(client, () => scope);
|
|
2244
|
+
uninstallGlobalHandlers = installGlobalHandlers(client, () => scope);
|
|
2091
2245
|
}
|
|
2092
2246
|
if (options.heartbeat) {
|
|
2093
2247
|
const heartbeatOptions = options.heartbeat === true ? {} : options.heartbeat;
|
|
2094
|
-
heartbeat = new Heartbeat(client, heartbeatOptions, options.instanceId);
|
|
2248
|
+
heartbeat = new Heartbeat(client, heartbeatOptions, options.instanceId, machineSampler);
|
|
2095
2249
|
heartbeat.start();
|
|
2096
2250
|
}
|
|
2097
2251
|
if (options.tasks) {
|
|
2098
2252
|
const tasksOptions = options.tasks === true ? {} : options.tasks;
|
|
2099
2253
|
agent = new TaskAgent(
|
|
2100
|
-
{ ...tasksOptions, config, instanceId: options.instanceId, debug: options.debug, client },
|
|
2254
|
+
{ ...tasksOptions, config, instanceId: options.instanceId, debug: options.debug, client, machineSampler },
|
|
2101
2255
|
registry
|
|
2102
2256
|
);
|
|
2103
2257
|
}
|