Skip to content

Observability

Observability is an important part of app development, maintenance, and sustainable scaling. It provides invaluable insight into both user behavior, and app stability and performance. Effective monitoring makes it easier to debug applications during development and in production. More importantly, it can help identify error-prone services or performance bottlenecks as apps scale.

Comprehensive observability solutions typically collect app data through two distinct (but related) channels: logging and telemetry. While their roles overlap, both are uniquely important for monitoring app health:

  • Logging: Messages and/or data recorded at key points in an app or service. Logs are especially useful for debugging, and visibility into specific workflow steps.
  • Telemetry: Higher-level data, like request duration or error rates, that provide a more holistic view of user behavior and app performance.

Whether used together or separately, both logging and telemetry are most effective when their output is aggregated in a monitoring platform. These dispatch alerts to report errors or unexpected behavior, and can be used to visualize performance data and other key metrics.

At its most basic, logging provides real-time insight into app internals, printing out current state as data moves through a service, as well as error messages and stack traces. It can also be used to generate a record of requests and their outcomes, acting as a bare-bones telemetry solution.

Logging tools typically support different log levels (e.g., debug, info, and error) that can be enabled (or silenced) in different environments. Debug logs, for example, usually aren’t generated in production in order to safeguard potentially sensitive data.

By default, Worker logs can be viewed in the Cloudflare dashboard, but they can also be pushed to a variety of third-party services for storage, analytics, or monitoring. Many logging tools offer similar features, allowing you to dispatch logs directly to observability or storage platforms.

Hono comes with a basic logger middleware that can be helpful for small apps or in the early stages of development. It prints a log at request start and end, including the path, method, status, and request time.

import { Hono } from "hono";
import { logger } from "hono/logger";
const app = new Hono()
.use("*", logger())
.get("/", (c) => c.text("HONC!"));
// <-- GET /
// --> GET / 200 0ms

Unlike third-party logging solutions, hono/logger can’t be used for ad hoc logs, and it only accepts strings. This significantly limits its value, especially for monitoring production apps.

A middleware integration for Pino logger exists, but is incompatible with Cloudflare.

LogTape is a slim logging tool with 0 dependencies and universal runtime support, making it a perfect fit for the Hono ecosystem. It’s also easily extensible, providing simple connections to telemetry tools like OpenTelemetry and Sentry.

With LogTape, you can print out string messages and/or structured data to the console, while also dispatching them to storage or observability solutions as necessary.

Structured logs aren’t magic—just JSON, but they make it substantially easier for observability and analytics platforms to parse key data.

To add LogTape to your Hono app, first configure your logger(s), then use the getLogger helper to access a logger instance by category (e.g., "my-app"). LogTape categories are used to create a hierarchy of loggers that can be configured separately. Note that if you skip the configuration step, your logs won’t be printed or dispatched.

import { configure, getConsoleSink, getLogger } from "@logtape/logtape";
await configure({
sinks: { console: getConsoleSink() },
loggers: [
{
category: "my-app",
lowestLevel: "debug",
sinks: ["console"]
},
],
});
const logger = getLogger(["my-app"]);
const app = new Hono()
.get("/", (c) => {
const path = c.req.path;
logger.debug("Hello from {path}!", { path });
return c.text("HONC!");
});

Check out LogTape’s Hono integration docs for a more robust example that logs key data like request duration and errors.

While some telemetry data can be generated by aggregating standard logs (e.g., request duration or error rates), comprehensive telemetry solutions go much deeper. They track what happens when a request is made, providing a high-level view into app performance and health.

Request behavior is captured by traces comprised of spans that represent individual units of work. A trace could have one or many spans, and spans can be nested to describe important sub-tasks. Together, they record data essential for understanding what’s happening as requests move through a service or application.

OTEL is a vendor-agnostic framework for generating and exporting telemetry data. While it can be used directly in an app, its spec and toolkit have become a standard that’s consumed by dozens of telemetry solutions (including Sentry).

As with standard logs, data collected using OTEL’s SDKs must be dispatched to in-house or third-party monitoring or analytics services. Hono’s OTEL middleware makes it easy to start collecting traces, though its design does not support custom spans or data collection.

To get started, just add the middleware and Cloudflare-specific instrumentation helper to your app, and specify where your telemetry data should be sent.

import { otel } from "@hono/otel";
import { instrument, ResolveConfigFn } from "@microlabs/otel-cf-workers";
import { Hono } from "hono";
const app = new Hono()
.use("*", otel())
.get("/", (c) => c.text("HONC!"));
const config: ResolveConfigFn = (env: Env, _trigger) => {
return {
exporter: {
url: "<MONITORING_SERVICE_URL>",
headers: { "x-api-key": env.MONITORING_API_KEY },
},
service: { name: "observability" },
}
};
export default instrument(app, config);

Combining telemetry and monitoring, Sentry is a robust observability solution. It uses OTEL under the hood to record events, errors, and other trace data, which is then sent to the Sentry platform. Sentry aggregates traces in customizable dashboards, and can send alerts to notify you when errors (or other important events) occur.

Hono’s Sentry middleware collects and dispatches data using Toucan, a Sentry client written for Workers that extends @sentry/core. The middleware sets the client in Hono’s Context, leaving you free to collect whatever data is relevant to your use-case.

import { sentry } from "@hono/sentry";
import { Hono } from "hono";
const app = new Hono()
.use("*", sentry())
// ...
.onError((error, c) => {
c.var.sentry.captureException(error);
return c.text("Internal Server Error", 500);
});
export default app;

The client sends traces to the URL set in the SENTRY_DSN secret, usually associated with your Sentry project. For more information, refer to the Sentry DSN docs.

Terminal window
SENTRY_DSN="<YOUR_DATA_SOURCE_NAME>"

Structured data can be sent to a variety of tools and platforms for analysis, visualization, or alerts. Here are just a few to consider:

  • BetterStack: A monitoring and analytics service that ingests logs or OTEL data, and offers configurable alerts and dashboards.
  • **DataDog:** An observability platform that can process logs, OTEL data, or events and metrics sent using its own SDK.
  • **Prometheus:** An independent open-source monitoring and alerting tool that analyzes app metrics—not logs or events.

Copyright © 2025 Fiberplane