Sharpen pilot logging: ISO timestamps, level labels, transport-error classification, per-frame info
- Emit ISO-8601 timestamps and string level labels (info/warn/...) so Portainer's log viewer renders seconds and human-readable levels. - Classify ETIMEDOUT/ECONNRESET/EPIPE/ENOTCONN as info one-liners rather than warns with stack traces. These are routine on cellular. - Add an info "frame ingested" line per accepted AVL frame so device activity is visible at info level until task 1.10 wires up prom-client.
This commit is contained in:
@@ -134,6 +134,19 @@ export function createTeltonikaAdapter(options: TeltonikaAdapterOptions): Adapte
|
|||||||
'malformed frame; dropping connection',
|
'malformed frame; dropping connection',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
err instanceof Error &&
|
||||||
|
'code' in err &&
|
||||||
|
// Routine on cellular: NAT timeouts, carrier RST, half-closed pipes.
|
||||||
|
// Surface as info (one-liner, no stack) so warns mean something.
|
||||||
|
['ETIMEDOUT', 'ECONNRESET', 'EPIPE', 'ENOTCONN'].includes(
|
||||||
|
(err as NodeJS.ErrnoException).code as string,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
sessionLogger.info(
|
||||||
|
{ code: (err as NodeJS.ErrnoException).code },
|
||||||
|
'session ended (transport error)',
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
sessionLogger.warn({ err }, 'unexpected error reading frame; dropping connection');
|
sessionLogger.warn({ err }, 'unexpected error reading frame; dropping connection');
|
||||||
}
|
}
|
||||||
@@ -204,6 +217,13 @@ export function createTeltonikaAdapter(options: TeltonikaAdapterOptions): Adapte
|
|||||||
result: 'ok',
|
result: 'ok',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Pilot-stage visibility into per-frame ingest. Downgrade to debug
|
||||||
|
// once task 1.10 wires up prom-client and frames_total is scrapeable.
|
||||||
|
sessionLogger.info(
|
||||||
|
{ codec: codecLabel, records: result.recordCount },
|
||||||
|
'frame ingested',
|
||||||
|
);
|
||||||
|
|
||||||
// ACK: 4-byte big-endian record count
|
// ACK: 4-byte big-endian record count
|
||||||
const ack = Buffer.alloc(4);
|
const ack = Buffer.alloc(4);
|
||||||
ack.writeUInt32BE(result.recordCount, 0);
|
ack.writeUInt32BE(result.recordCount, 0);
|
||||||
|
|||||||
@@ -22,10 +22,19 @@ export function createLogger(options: {
|
|||||||
instance_id: instanceId,
|
instance_id: instanceId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Emit `"level":"info"` instead of pino's default `"level":30` so log
|
||||||
|
// viewers (Portainer, etc.) show a human-readable label rather than the
|
||||||
|
// numeric level.
|
||||||
|
const formatters = {
|
||||||
|
level: (label: string) => ({ level: label }),
|
||||||
|
};
|
||||||
|
|
||||||
if (nodeEnv === 'development') {
|
if (nodeEnv === 'development') {
|
||||||
return pino({
|
return pino({
|
||||||
level,
|
level,
|
||||||
base,
|
base,
|
||||||
|
timestamp: pino.stdTimeFunctions.isoTime,
|
||||||
|
formatters,
|
||||||
transport: {
|
transport: {
|
||||||
target: 'pino-pretty',
|
target: 'pino-pretty',
|
||||||
options: {
|
options: {
|
||||||
@@ -37,6 +46,8 @@ export function createLogger(options: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Production and test: plain JSON — fast, no extra deps
|
// Production and test: plain JSON — fast, no extra deps.
|
||||||
return pino({ level, base });
|
// ISO-8601 string timestamps (vs default epoch-ms) survive downstream
|
||||||
|
// log renderers (Portainer, Docker --timestamps) without losing seconds.
|
||||||
|
return pino({ level, base, timestamp: pino.stdTimeFunctions.isoTime, formatters });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user