1.Containers: pino to stdout
pino writes one JSON object per line with msg, level (numeric) and time (epoch ms). Add your service fields once as base bindings.
logger.js
import pino from "pino";
export const log = pino({
base: { service: "checkout", env: process.env.NODE_ENV ?? "prod" },
formatters: { level: (label) => ({ level: label }) }, // "info" instead of 30
timestamp: pino.stdTimeFunctions.isoTime, // "time":"2026-09-04T11:42:08.877Z"
});
log.info({ orderId: "4711", durationMs: 812 }, "payment authorized");Note: With the Vector examples, add
_time_field: time (already parsed by parse_json) and _msg_field: msg to the sink query.2.No agent: a small batching transport (no dependencies)
Buffers lines and flushes every two seconds or 200 lines; uses the JSON-lines endpoint. Works as a pino destination or on its own.
uptimeeye-logs.js
const ENDPOINT = "https://logs.uptimeeye.com/v1/ingest/jsonline?_msg_field=msg&_time_field=time&_stream_fields=service,env";
const KEY = process.env.UPTIMEEYE_INGEST_KEY;
let buffer = [];
let timer = null;
export function send(entry) {
buffer.push(JSON.stringify({ time: new Date().toISOString(), service: "checkout", env: "prod", ...entry }));
if (buffer.length >= 200) return flush();
timer ??= setTimeout(flush, 2000);
}
export async function flush() {
clearTimeout(timer); timer = null;
if (buffer.length === 0) return;
const body = buffer.join("\n") + "\n";
buffer = [];
try {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/x-ndjson" },
body,
});
if (!res.ok) console.error("uptimeeye logs:", res.status, await res.text());
} catch (err) {
console.error("uptimeeye logs:", err);
}
}
process.on("beforeExit", flush);
// usage
send({ level: "info", msg: "payment authorized", orderId: "4711", durationMs: 812 });3.OpenTelemetry logs SDK
otel.js
import { logs } from "@opentelemetry/api-logs";
import { LoggerProvider, BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";
const provider = new LoggerProvider({
resource: resourceFromAttributes({ "service.name": "checkout", "deployment.environment": "prod" }),
processors: [
new BatchLogRecordProcessor(
new OTLPLogExporter({
url: "https://logs.uptimeeye.com/v1/ingest/otlp/v1/logs",
headers: { Authorization: `Bearer ${process.env.UPTIMEEYE_INGEST_KEY}` },
}),
),
],
});
logs.setGlobalLoggerProvider(provider);
logs.getLogger("checkout").emit({ severityText: "INFO", body: "payment authorized", attributes: { orderId: "4711" } });Note: pino users can keep pino and add
pino-opentelemetry-transport to feed the same exporter.Fields you get
These show up in the fields panel and can be used in every filter:
service,envlevelmsgas the messagepid,hostname(pino defaults)- every object key you log
Tips
- Log objects, not string concatenation:
log.info({ orderId }, "…")gives you a filterableorderIdfield. - winston:
format.combine(format.timestamp(), format.json())produces the same shape; set_msg_field=message&_time_field=timestamp. - Serverless: flush on the platform's shutdown hook (Lambda: at the end of the handler) — there is no
beforeExityou can rely on.
FAQ
- Should I send from the process or from an agent?
- From an agent whenever one exists: it survives crashes, batches across processes and needs no key in the application. Send directly only where no agent can run.
- Where do I get the ingest key?
- In the app under API Keys → New API Key → type “Log ingest”. The key starts with
ue_ingest_and is shown once. Management keys (ue_live_) are refused by the ingest endpoint. - How do I check that logs arrive?
- Open Logs in the app, pick the 15m range and search for
service:=<your service>. New lines are searchable within about a second; Live tail shows them with a ~6 s delay.
Related
HTTP API / curlThe UptimeEye Logs ingest API: send JSON lines with curl or your own code, choose stream fields, compress with gzip and read the response codes..OpenTelemetryPoint an OpenTelemetry Collector or any OTel SDK at UptimeEye Logs over OTLP/HTTP.DockerSend the logs of all containers on a Docker host to UptimeEye Logs: one Vector container on the Docker socket, or the Loki logging driver per container..Searching logsNow that lines arrive: filters, fields, time ranges and live tail.