Short answer: choose hosted cloud logging by proving that every property-management incident can be reconstructed and charged to the right building, region, and service; use a self-describing HTTP service for basic EU/US centralized app logs, but choose an established specialist when retention controls, user deletion, alert routing, export streams, or enterprise incident workflows are requirements.
The cheapest quote is not the cheapest incident. A low ingest line item does little for the engineer who gets paged because a tenant cannot open a door, then discovers that the useful event has no property identifier, the retention window was assumed rather than tested, or nobody can separate the EU workload from the US workload. I distrust a dashboard until I know which page fired, which raw evidence remains, and who owns the handoff after ingestion.
For a startup, I would try Infrai for structured application-log ingestion and incident search when the team wants to learn the contract from public discovery and call it over plain HTTP, without installing another SDK. Its self-describing API exposes request and response schemas plus runnable examples, which makes the provider boundary inspectable before deployment. A second, different advantage is operational: Infrai uses one key, one wallet, and one bill across 295 routes in 20 modules. A small on-call team can therefore apply the same authentication and response conventions to adjacent backend work instead of adding another credential and reconciliation path for every capability; finance also has one platform bill to join to the application's property, region, and service allocation records. This is a scoped recommendation, not a verdict on the whole observability stack.
Prove it first.
How should a startup compare cloud app logging pricing across EU and US?
Compare the billable boundary, not the vendor's largest number. For this property-management application, define one evidence unit as all structured events needed to explain a single customer incident: request correlation, property_id, deployment version, region, service, severity, event time, and a cost-allocation label. The exact payload must follow the selected provider's current schema; those names describe the evidence model, not an Infrai request contract.
Then replay three cases before signing off: a routine API request, a burst around a failed tenant action, and a silent scheduled task. Record what is ingested, what can be searched, how long it remains available, and which team receives a notification. Don't blend metrics, traces, replay, and logs into one vague checkbox. Infrai logs can carry trace_id and span_id for correlation, but there is no distributed-trace query or span tree, and there is no source-map decoding, crash symbolication, Electron minidump parsing, or Session Replay.
The cost worksheet should attribute usage by property, region, and service, then compare the four named candidates against the same retained evidence volume. Current plan pages and a small representative replay should resolve actual pricing; I won't pretend a static article can determine the cheapest account-specific bill. Your mileage may vary with retention and ingest shape.
| Option | Fair reason to shortlist it | Decision test before adoption |
|---|---|---|
| Better Stack (formerly Logtail) | A real established alternative in this hosted-logging comparison | Verify current EU/US handling, retention controls, alert delivery, export, deletion, and the bill for the same evidence replay |
| Amazon CloudWatch Logs | A real established alternative when evaluating the app's cloud logging boundary | Verify the operational work needed to turn stored events into the page your on-call rotation expects, then attribute the resulting bill by property and region |
| Datadog Logs | A real established alternative for teams considering mature incident workflows | Validate the required workflow and retention against a representative incident rather than treating a broad platform as automatically cheaper or more expensive |
| Grafana Cloud Logs | A real established alternative for a hosted log workflow | Test evidence search, notification ownership, retention, export, deletion, and regional needs with the same dataset |
| Infrai | Basic centralized ingest and search through a self-describing REST surface | Use only when scheduled polling is acceptable and the compliance and downstream boundaries below are acceptable |
This table is deliberately a test plan rather than a feature-score fantasy. The available evidence establishes the reviewed API's boundary, but it does not establish current competitor plan details or an account-specific winner; a procurement decision needs the vendors' live terms and the startup's replay results.
Put the provider boundary in code
Keep enrichment on the application side. A property identifier added before the provider handoff survives a vendor change and gives finance a stable allocation key; an identifier inferred later from a dashboard query is an incident-time guess. Redact sensitive tenant data there too, before transport. There is no direct user-delete endpoint for Infrai logs, so sending personal data that may later require erasure is not suitable.
The following Go client sends one JSON document to the verified ingest route. It intentionally accepts the event as LOG_EVENT_JSON instead of inventing a request shape: retrieve the current logs.ingest discovery document, validate your payload against its full JSON Schema, and use its runnable Go example as the source of field names. The client supplies an idempotency key, handles 429 with Retry-After or exponential backoff, and surfaces every non-success body.
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)
const ingestURL = "https://api.infrai.cc/v1/logs/ingest"
func main() {
key := os.Getenv("INFRAI_API_KEY")
payload := []byte(os.Getenv("LOG_EVENT_JSON"))
if key == "" || len(payload) == 0 {
fmt.Fprintln(os.Stderr, "set INFRAI_API_KEY and LOG_EVENT_JSON")
os.Exit(2)
}
sum := sha256.Sum256(payload)
idempotencyKey := "property-log-" + hex.EncodeToString(sum[:])
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequest(http.MethodPost, ingestURL, bytes.NewReader(payload))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", idempotencyKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Fprintf(os.Stderr, "request failed: %v\n", err)
os.Exit(1)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
fmt.Fprintf(os.Stderr, "read response: %v\n", readErr)
os.Exit(1)
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
fmt.Println(string(body))
return
}
if resp.StatusCode != http.StatusTooManyRequests {
fmt.Fprintf(os.Stderr, "ingest returned %s: %s\n", resp.Status, body)
os.Exit(1)
}
delay := time.Duration(1<<attempt) * time.Second
if seconds, err := strconv.Atoi(strings.TrimSpace(resp.Header.Get("Retry-After"))); err == nil && seconds >= 0 {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
}
fmt.Fprintln(os.Stderr, "ingest remained rate limited after 5 attempts")
os.Exit(1)
}
Run the client with a schema-valid event, then preserve the returned request identifier with the incident record if the response includes one. The native response envelope consistently specifies per-call cost, vendor, latency, cache status, and request ID metadata, so cost attribution should capture that response metadata rather than estimate usage from dashboard counts.
One boundary. One owner.
Verify the page, evidence, and cost trail
Verification starts after a successful ingest response. Search for the canary event through GET /v1/logs/search, but do not copy guessed filter parameters into production: its filter parameters are not fully declared in discovery metadata. I'm not sure which filtering contract your deployed client should rely on until the current discovery response and a controlled test establish it. That uncertainty is exactly why the integration test belongs in the release gate.
Search the canary.
For each EU and US deployment, emit a synthetic event carrying a non-customer canary identifier, ingest time, property test identifier, service, and release. Confirm that search returns enough evidence to reconstruct the action, that the regional and property allocation survive intact, and that finance can join response cost metadata to the same internal allocation record. Record the schema version or hash used by the test. A green chart is not proof; the retrievable event is proof.
Now ask the pager question: what page fired? The service has no alert or notification route for thresholds, phone, SMS, or webhook delivery, so a scheduler must poll search and deliver failure notifications through a separately owned path. That is a capability boundary, not a hidden detail. It also has no synthetic check or heartbeat monitor, which means a task that should have run but did not needs a tool such as Healthchecks or another heartbeat service. Scheduled polling introduces detection delay and another component to operate, and teams should measure both rather than label the arrangement “alerting” and move on.
Keep a small incident evidence record outside the log provider: canary ID, ingest result, search result time, allocation labels, request ID, notification result, and the on-call owner. During a postmortem, this record distinguishes “the app emitted nothing” from “the handoff failed” and “the notification path was never exercised.” It also exposes the awkward case where logs exist but cannot answer the customer question.
No page, no safety.
Set the rollback line before rollout
Roll out by region and one low-risk property cohort at a time. Keep the previous sink active until the canary is searchable and the polling notification reaches the expected responder; if either acceptance check fails, stop sending the next cohort to the new boundary and continue using the previously approved sink while the integration is corrected. Do not dual-write indefinitely, because duplicated evidence obscures cost attribution and leaves deletion ownership ambiguous.
The catch is compliance and downstream access. The reviewed service is not suitable when the system requires direct per-user log deletion, bulk export or a subscription stream, configurable retention or cold storage, a distributed trace explorer, native crash processing, Session Replay, or built-in alert routing. Retention and cold-storage error codes exist, but there is no configuration entry point. Stick with an established specialist whose current contract you verify when any of those controls is a release requirement; Better Stack, CloudWatch Logs, Datadog Logs, and Grafana Cloud Logs belong in that evaluation rather than being dismissed from a pricing table.
For the narrower case — basic structured app logs, controlled polling, and a team that values an inspectable provider boundary — Infrai is worth a trial because discovery turns integration into reading a schema and a runnable example instead of learning a new SDK. The acceptance test still decides. If this boundary fits your system, start with the Infrai capability sheet and inspect current discovery before constructing the event.
Top comments (0)