All files / src/constants Events.ts

100% Statements 1/1
100% Branches 0/0
100% Functions 0/0
100% Lines 1/1

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192                                                                                        28x                                                                                                                                                                                                                                                                                                      
/**
 * @constant
 *
 * The event names every Worker emits, whatever it is for.
 *
 * @remarks
 * An event name is the field a log query filters on, so the value of one is
 * entirely in its being spelled the same way twice. `request.failed` and
 * `request-failed` are two events to a collector and one to a reader, and the
 * second only ever shows up as a gap in a chart nobody thinks to question.
 * These constants exist to make the spelling unavailable to typos.
 *
 * ## The convention
 *
 * `subject.outcome`, lowercase, dotted, `snake_case` within a segment:
 * `external_api.error`, `invoice.fetch.completed`. The subject is what the
 * event is *about* and the outcome is what became of it.
 *
 * **The subject is never the Worker.** Workers Logs already records which
 * script wrote a line and offers it as a column, so `example-worker` in front
 * of `order.sent` buys nothing and costs the query `event = order.sent` its
 * meaning across services. `service` says where
 * it happened; `event` says what happened; the two are read together.
 *
 * ## What belongs here, and what does not
 *
 * Only the cross-cutting names — the ones that mean the same thing in every
 * Worker, so that "error rate by event" is answerable across deployments. A
 * Worker's own vocabulary is its own: `order.sent`, `message.received`, and
 * `invoice.fetch.completed` are meaningful exactly once each and belong beside
 * the code that emits them, as string literals. Hoisting them here would centralise a list nothing shares.
 *
 * @example
 * ```ts
 * import { events } from '@bayudwiyansatria/cloudflare'
 *
 * logger.error(events.EXTERNAL_API_ERROR, { provider: 'telegram', status })
 * logger.info('notification.sent', { notificationId, durationMs })
 * ```
 *
 * @author Bayu Dwiyan Satria
 * @version 1.2.0
 * @since 1.2.0
 */
export const events = {
  /**
   * A request finished and produced a response — any status below `500`.
   *
   * @remarks
   * Emitted once per request by
   * [`logToAnalytics`](../middlewares/logger.ts), at `info` for a success and
   * `warn` for a `4xx`. The level carries the outcome so the event does not
   * have to: one name means "a request ended", and `status` in the payload says
   * how.
   */
  REQUEST_COMPLETED: 'request.completed',
 
  /**
   * A request ended in a fault — a `5xx`, or an exception nothing caught.
   *
   * @remarks
   * The counterpart to {@link events.REQUEST_COMPLETED}, and the one line a
   * production investigation starts from. Split out rather than folded into the
   * status field because "how often is this Worker failing" should be a filter
   * on one value, not a range query.
   */
  REQUEST_FAILED: 'request.failed',
 
  /**
   * An exception reached the application's error boundary.
   *
   * @remarks
   * Distinct from {@link events.REQUEST_FAILED}, and the pair is deliberate.
   * Inside a Hono app the request middleware never sees the exception — the
   * framework has already turned it into a `500` by the time `next()` returns —
   * so the only place the stack exists is `app.onError`. One name would make
   * every unhandled failure count twice; two names keep `request.failed` an
   * honest count of failed requests and give the stack a filter of its own.
   *
   * Both lines carry the same request id, so one query on that id returns the
   * failure and its cause together.
   */
  REQUEST_UNHANDLED: 'request.unhandled',
 
  /**
   * A caller presented no credential, or the wrong one.
   */
  AUTHENTICATION_FAILED: 'authentication.failed',
 
  /**
   * A caller presented a credential this Worker accepted.
   *
   * @remarks
   * At `debug`. A successful authentication is not news at volume; it is worth
   * having when a specific caller's requests are being traced through a session
   * and the question is which identity they arrived under.
   */
  AUTHENTICATION_SUCCEEDED: 'authentication.succeeded',
 
  /**
   * A caller authenticated but may not do what they asked.
   */
  AUTHORIZATION_DENIED: 'authorization.denied',
 
  /**
   * A rate limiter refused a request.
   */
  RATE_LIMIT_EXCEEDED: 'rate_limit.exceeded',
 
  /**
   * A rate limiter could not be consulted, and the request was let through.
   *
   * @remarks
   * The limiter failing open is a deliberate choice — refusing every request
   * because the limiter is unreachable turns a degraded dependency into an
   * outage — but it is also the window in which the limit is not being
   * enforced, which is worth being able to see.
   */
  RATE_LIMIT_UNAVAILABLE: 'rate_limit.unavailable',
 
  /**
   * A D1 or Hyperdrive query failed.
   */
  DATABASE_ERROR: 'database.error',
 
  /**
   * A KV read or write failed.
   */
  KV_ERROR: 'kv.error',
 
  /**
   * An R2 operation failed.
   */
  R2_ERROR: 'r2.error',
 
  /**
   * A queue send or a batch handler failed.
   */
  QUEUE_ERROR: 'queue.error',
 
  /**
   * A metric could not be written to Analytics Engine.
   *
   * @remarks
   * At `debug` when the dataset is simply not bound, which is a deployment
   * decision rather than a fault, and at `warn` when a bound dataset rejected
   * the write. Telemetry never fails a request either way.
   */
  ANALYTICS_ERROR: 'analytics.error',
 
  /**
   * A call left this Worker for a service it does not own.
   *
   * @remarks
   * At `debug`, and only where the call is the interesting part of the flow.
   * Cloudflare already traces subrequests; this is for the application context
   * a span has no field for — which provider was chosen, and why.
   */
  EXTERNAL_API_REQUEST: 'external_api.request',
 
  /**
   * A call to a service this Worker does not own failed, or answered with an
   * error status.
   */
  EXTERNAL_API_ERROR: 'external_api.error',
 
  /**
   * An inference call was made to Workers AI or an AI gateway.
   */
  AI_REQUEST: 'ai.request',
 
  /**
   * An inference call failed.
   */
  AI_ERROR: 'ai.error',
 
  /**
   * A cron trigger began.
   */
  SCHEDULED_STARTED: 'scheduled.started',
 
  /**
   * A cron trigger finished its work.
   */
  SCHEDULED_COMPLETED: 'scheduled.completed',
 
  /**
   * A cron trigger failed.
   */
  SCHEDULED_FAILED: 'scheduled.failed'
} as const