Logs, metrics, and product analytics can all record that something happened. They serve different people and should not receive the same events automatically.
Logs explain one execution#
Logs help an engineer reconstruct a request or worker run:
logger.warn("notifications.delivery.deferred", {
correlationId,
provider: "push",
reason: "rate_limited",
attempt: 2,
});
They carry technical context and often have short retention. Structured fields make them searchable. Sensitive payloads still do not belong there.
Metrics reveal system health#
Metrics aggregate numeric behavior over time: error rate, latency, queue age, or the count of deferred deliveries.
metrics.count("NotificationDeliveryDeferred", 1, {
provider: "push",
reason: "rate_limited",
});
Dimensions must have bounded values. A provider dimension may have three values; a userId dimension may have millions. High-cardinality data belongs in logs or a purpose-built trace system.
Metrics are best when someone can define a threshold and response: page the on-call engineer, scale a consumer, or inspect a provider outage.
Product analytics explains user behavior#
Analytics answers questions about the product journey: Did people finish onboarding? Which feature leads to retention? It should represent meaningful user actions, not implementation steps.
analytics.track(userId, "course_completed", {
courseId,
completionDays,
});
Do not send database_query_finished to a product tool. Likewise, a rare business conversion should not exist only as a temporary operational log.
One fact may create more than one signal#
A failed payment can produce a detailed error log, increment a failure metric, and create a product event if that failure matters to funnel analysis. Each signal should contain only the fields needed for its audience and retention policy.
Before emitting telemetry, ask who will use it and what decision it enables. That question prevents noisy logs, expensive metrics, and analytics dashboards filled with backend implementation details.