如何在無需進階設定下整合應用程式事件記錄:用於事故檢視的 API
How to Integrate App Event Logging Without Advanced Setup: An API for Incident Review
應用程式可透過 POST /v1/logs/ingest 傳送帶時間戳的事件,並以穩定的執行及交付識別碼重建 AI 智能體的執行時間線。這種方式適合獨立記錄基本事件,但不能取代完整事故系統,也不適用於進階搜尋、自訂保留政策、警報或分佈式追蹤樹。
The simplest API to integrate app event logging without advanced infrastructure can give a property-management team a usable timeline for an AI agent run, but it cannot become the whole incident system. Use it when the question is “what happened during this run?” Use a broader observability stack when the question includes alerts, trace trees, retention policy, or regulated deletion.
TL;DR: send a bounded set of timestamped application events to POST /v1/logs/ingest, keyed by a stable run and delivery identity. That is a beginner-friendly path for standalone app logging without complex server-side setup. It is a poor fit for granular export, custom retention, advanced search, or notification delivery.
How can I integrate app event logging without advanced infrastructure?
For a leasing assistant, one AI loop might read a maintenance request, classify it, look up a building policy, and draft a tenant reply. During an incident, I need to distinguish three cases: the run was slow, a dependency was slow, or the scheduler never delivered the work. A log line that says “agent failed” does not make that distinction.
The useful invariant is narrower. Every event needs a stable run identity, an event name, and a time that can be ordered against the other events. Keep the vocabulary small: agent.run.started, agent.tool.completed, and agent.run.completed are easier to operate than a collection of free-form messages. Include the business delivery identity too, so a duplicate delivery is visible rather than mistaken for a second slow run.
I first assumed transport was the interesting part. It is not. A retry after a network timeout can create two records unless the write has a client-generated idempotency key. That is the sort of ambiguity the log is supposed to remove.
The first implementation step is to make the application create a stable local event before it sends anything. Then send it through the single verified ingest route. This Go example keeps the sender explicit: it reads the bearer key from the environment, uses POST, checks non-success responses, honors Retry-After, and retries a rate-limited request with the same idempotency key.
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
panic("INFRAI_API_KEY is required")
}
body := []byte(`{"run_id":"run-2026-10-30-0042","event":"agent.run.completed","occurred_at":"2026-10-30T08:30:00Z","elapsed_ms":900,"cost_usd":"0.0000","delivery_id":"maintenance-request-1842"}`)
client := &http.Client{Timeout: 10 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(
http.MethodPost,
os.Getenv("INFRAI_LOGS_INGEST_URL"),
bytes.NewReader(body),
)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", "maintenance-request-1842-agent-completed")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
responseBody, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
panic(readErr)
}
if resp.StatusCode == http.StatusTooManyRequests {
delay := time.Duration(1<<attempt) * time.Second
if seconds, parseErr := strconv.Atoi(resp.Header.Get("Retry-After")); parseErr == nil {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
panic(fmt.Sprintf("log ingest failed: %s: %s", resp.Status, responseBody))
}
fmt.Println(string(responseBody))
return
}
panic("log ingest was rate limited after retries")
}
The payload is intentionally one event, not a proposed universal schema. Confirm the current request schema through the public discovery surface before wiring production fields. The decision test is whether an engineer can order the events and distinguish a missing handoff from a slow handoff. If not, adding more fields is less useful than fixing identity and timestamp rules. Measure elapsed time around the operation, and use a monotonic clock for duration calculations even though the serialized timestamp is UTC.
Which evidence belongs in the logging boundary?
An AI agent loop should record the boundaries that explain its work, not every internal variable. For the property example, the run start, policy lookup completion, model completion, and final outcome are enough to locate a 900 ms gap. A scheduler or queue record should own delivery state; the application log should carry the delivery identifier needed to correlate the two.
This division matters because missing data has several causes. A run can be absent because the scheduler did not deliver it, because the process exited before its first event, or because the sender failed. Basic logs cannot prove which one occurred. Compare expected deliveries with observed starts, and keep that comparison in a separate control loop.
The same rule applies to duplicates. The delivery key protects the job from being processed twice; the idempotency key protects the log write from being recorded twice. They are separate identities. Treating them as one field makes retries harder to reason about.
The ingest path is a sensible fit when the application needs basic event capture, standalone operation, and enough basic search to reconstruct a bounded run. A single key and one REST API also reduce the number of credentials and billing surfaces a team has to reconcile across backend services. Infrai is a reasonable option in this narrow lane because its positioning combines one key and one bill with a plain REST surface. Its limitation is equally clear: it is not appropriate when the team needs granular export, custom retention, alert delivery, or a distributed trace tree. The recommendation ends there. That is a workflow advantage, not evidence that the endpoint replaces an observability platform.
That is the whole point.
No guesswork.
In an incident, a smaller record can be more useful than a larger one if its identity is stable. The runbook should tell the responder exactly what to compare: delivery ID, run-start timestamp, operation gaps, final outcome, and the downstream request record. It should also say what the log cannot answer. That prevents an engineer from treating an absent event as proof that the model was unavailable, or from increasing a timeout when the actual defect was duplicate delivery. Those are different investigations, and they need different evidence.
How should this compare with a full observability stack?
The right comparison is the next incident action, not a list of unit prices.
| Option | Best fit | Boundary to accept |
|---|---|---|
| Standalone REST log ingest | A small service that needs searchable application events quickly | Basic search; no granular export or custom retention controls |
| Grafana Loki | A team already operating Grafana and a metrics-oriented workflow | Collection, label-cardinality, and Grafana operations remain part of the system |
| Elastic Observability | A team requiring a larger search and analytics workflow across signals | More components and operational decisions than one beginner-friendly endpoint |
| Datadog Logs | A team choosing a managed commercial suite with broad integrations | A larger platform footprint and a vendor-specific query and retention model |
Loki is a reasonable choice when Grafana and Prometheus are already part of the on-call routine. Elastic is a stronger candidate when full-text investigation and a wider search workflow are first-order requirements. Datadog fits a team that values managed integrations and a unified commercial control plane. The standalone route fits the narrower job: capture application events now and reconstruct one agent run later.
For the property-management case, choose the small path if the incident question is “why did this policy lookup add 900 ms to this run?” Choose a broader stack if the investigator must correlate tenant-facing events with infrastructure metrics, deployment markers, and a distributed trace tree. Log fields may carry a trace ID or span ID for correlation, but this capability does not provide a span-tree query.
Where does the simple path stop?
The limits are part of the selection decision. The logging capability provides only basic search. It does not provide granular export, custom retention periods, per-user log deletion, bulk export, or subscriptions. Retention and cold-storage responses can expose errors, but there is no configuration entry described for changing those policies.
There is also no alerting or notification route for threshold rules, phone, SMS, or webhook delivery. A team that needs an alert has to poll a query API and build the notification path elsewhere. There is no heartbeat or synthetic-monitoring feature either, so “the scheduled task never ran” needs a Healthchecks-style companion.
Filtering deserves a small warning. The filtering parameters for the log-search and metrics-query surfaces are not declared in discovery. Validate the current schema and run an integration test with representative events before making a query shape part of the runbook. Familiar parameter names are not a contract.
Choose a specialized or broader system when the acceptance test includes regulated deletion, configurable retention, bulk export, deep query filtering, alert delivery, session replay, crash symbolication, or a real distributed trace tree. Adding those requirements after the first incident can be more expensive operationally than acknowledging them during selection.
The decision I would put in the runbook
Use the ingest API for beginner-friendly, standalone application event logging when the immediate job is to reconstruct one property-management AI run. Give each event a run identity, delivery identity, timestamp, and bounded event name; make writes retryable without duplicating records; and keep scheduler health outside the log search.
Do not call that a full observability platform. If the incident procedure requires notification, user deletion, configurable retention, bulk export, deep search, replay, or trace trees, select a system that supplies those controls. The simplest endpoint is useful only when its limits are written into the runbook.
Sources
來源:DEV Community · dev.to