feat(server): add heartbeat gauge + first-export-attempt log for OTLP (#20230)
## Summary - Adds a trivial always-on observable gauge (`twenty.heartbeat = 1`) so the OTLP exporter fires on every 10s collection tick, even on idle pods. Without this, `PeriodicExportingMetricReader` skips the export when `scopeMetrics` is empty, so the process-log wrapper never runs and we can't prove OTLP connectivity. - Adds a one-time "first periodic export attempt" log line inside the wrapped exporter, completing the startup log sequence: `OTLP reader enabled` → `first periodic export attempt` → `first export ok` / `export failed`. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -70,12 +70,19 @@ const serializeOtlExportFailure = (error: unknown): string => {
|
||||
};
|
||||
|
||||
let otlpMetricsFirstExportSuccessLogged = false;
|
||||
let otlpMetricsFirstExportAttemptLogged = false;
|
||||
|
||||
const wrapOtlMetricExporterWithProcessLogs = (
|
||||
inner: OTLPMetricExporter,
|
||||
otlpEndpointForLog: string,
|
||||
): PushMetricExporter => ({
|
||||
export(metrics: ResourceMetrics, resultCallback) {
|
||||
if (!otlpMetricsFirstExportAttemptLogged) {
|
||||
otlpMetricsFirstExportAttemptLogged = true;
|
||||
console.log(
|
||||
`${OTLP_METRICS_LOG_PREFIX} first periodic export attempt | endpoint=${otlpEndpointForLog}`,
|
||||
);
|
||||
}
|
||||
const totalMetricData = metrics.scopeMetrics.reduce(
|
||||
(accumulator, scopeMetric) => accumulator + scopeMetric.metrics.length,
|
||||
0,
|
||||
@@ -177,3 +184,19 @@ const meterProvider = new MeterProvider({
|
||||
});
|
||||
|
||||
opentelemetry.metrics.setGlobalMeterProvider(meterProvider);
|
||||
|
||||
// Always-on gauge so the OTLP exporter fires on every collection tick,
|
||||
// even when the process is otherwise idle. This guarantees the process-log
|
||||
// wrapper above can prove connectivity (first export ok / export failed).
|
||||
if (meterDrivers.includes(MeterDriver.OpenTelemetry)) {
|
||||
const heartbeatMeter = opentelemetry.metrics.getMeter(
|
||||
'twenty-server-heartbeat',
|
||||
);
|
||||
const heartbeat = heartbeatMeter.createObservableGauge('twenty.heartbeat', {
|
||||
description: 'Always-on gauge (1) to prove OTLP export pipeline',
|
||||
});
|
||||
|
||||
heartbeat.addCallback((observableResult) => {
|
||||
observableResult.observe(1);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user