AlphornAlphorn Docs

Application Error Tracking

Route application errors and exceptions to the right team in real time, with examples for Express.js, Python, and frontend JavaScript errors.

Application errors need immediate attention - but not all errors are equal. Route critical errors to PagerDuty while sending warnings to Slack, without configuring each service individually.

Express.js error handler

app.use((err, req, res, next) => {
  const status = err.status ?? 500;
  const priority = status >= 500 ? 5 : 3;

  fetch("https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.ALPHORN_API_KEY}`,
    },
    body: JSON.stringify({
      title: `${err.name}: ${err.message}`,
      message: `${req.method} ${req.path}\n\nStack:\n${err.stack?.slice(0, 500)}`,
      priority,
      tags: ["error", `http-${status}`, "backend"],
    }),
  }).catch(() => {}); // Don't block the error response

  // 4xx errors are the client's fault, so tell them what went wrong; hide 5xx details
  res.status(status).json({ error: status < 500 ? err.message : "Internal server error" });
});

Python exception handler

import os
import traceback
import requests

def notify_error(exc, context=""):
    try:
        requests.post(
            "https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT",
            headers={"Authorization": "Bearer " + os.environ["ALPHORN_API_KEY"]},
            json={
                "title": f"{type(exc).__name__}: {str(exc)[:100]}",
                # the end of the traceback is the most useful part
                "message": f"{context}\n\n{traceback.format_exc()[-1500:]}",
                "priority": 5,
                "tags": ["error", "exception", "backend"],
            },
            timeout=5,
        ).raise_for_status()
    except requests.RequestException:
        pass  # never let a failed notification hide the original error

# Usage
try:
    process_payment(order)
except Exception as e:
    notify_error(e, context=f"Processing order {order.id}")
    raise

Frontend errors

Alphorn doesn't send CORS headers, so a browser on your site can't POST to it directly. Send frontend errors to your own backend instead, and let the backend forward them to Alphorn. That also keeps the API key on the server:

browser
window.addEventListener("error", (event) => {
  fetch("/client-errors", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      message: event.message,
      source: `${event.filename}:${event.lineno}`,
    }),
  });
});
server (Express)
app.post("/client-errors", express.json(), (req, res) => {
  const { message, source } = req.body;

  fetch("https://app.alphorn.dev/n/V1StGXR8_Z5jdHi6B-myT", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.ALPHORN_API_KEY}`,
    },
    body: JSON.stringify({
      title: `Frontend error: ${message}`,
      message: `${source}\n\nUser-Agent: ${req.get("user-agent")}`,
      priority: 3,
      tags: ["error", "frontend"],
    }),
  }).catch(() => {});

  res.sendStatus(204);
});

This endpoint is public, so rate-limit it before you route its messages anywhere noisy.

Routing examples

ChannelFilterPurpose
Slack (#errors)Tags has any of errorAll errors visible to the team
PagerDutyPriority greater than 4 and tags has any of errorPage on-call for 500s
Slack (#frontend)Tags has any of frontendFrontend-specific errors
EmailTags has any of errorFull error archive

On this page