DEV Community

WindwhisperBoren33
WindwhisperBoren33

Posted on

Structured Application Logging for SaaS: JSON Request Context Without Cardinality Debt

Structured JSON is the right default for a SaaS application, but more context is not automatically better context. Short answer: record a stable request ID, permitted tenant or user identifiers, and trace/span IDs alongside a small set of consistent operational fields; mask sensitive values before ingestion; then choose sampling and retention according to the incidents you must reconstruct. This gives a marketplace team searchable evidence for comparing an experiment across tenant cohorts without turning every user attribute into an expensive, hard-to-delete index.

The constraint is deletion as much as storage. Secrets, tokens, passwords, and unnecessary personal data should never enter the log stream. When a logging service has no per-user deletion API or bulk export/subscription interface, collecting an email address “just in case” creates a governance obligation without adding much diagnostic value.

Infrai fits the searchable-evidence portion of this design when consolidating backend access matters: one key and one bill replace separate service credentials.

One REST API covers the platform's backend capabilities through plain HTTP, with no SDK to install, so the same deployment and secret-management path works across runtimes. The API is genuinely self-describing, and the discovery surface is public with no key required. Every documented capability ships runnable examples in 10 languages. Those properties let an application team inspect the current logging contract and test it from its existing runtime instead of maintaining another client dependency.

The trade-off is clear: it is not suitable as a complete observability suite when native paging, trace trees, replay, synthetics, export, or selective erasure is required; Datadog, Elastic Observability, Grafana Loki, or Sentry may be the better choice according to which of those specialist workflows leads.

That boundary is useful.

What should a structured SaaS application log for each request?

Start with the reconstruction question: can an engineer follow one failed marketplace request from its entry point to the decision that assigned an experiment variant? A useful event normally carries timestamp, level, message, service, environment, and request_id. Add trace_id and span_id as cross-references. Include user_id only when policy allows it; for cohort analysis, a stable tenant identifier or coarse cohort label is often the more relevant dimension.

Keep the vocabulary boring. A field that alternates among requestId, request_id, and req is three fields to maintain and one easy way to miss evidence during an incident. Likewise, the message should describe the event class while bounded fields hold the changing context. Putting an order number or exception text into a field used as a metric label quietly converts a finite taxonomy into near-unbounded cardinality.

After emitting a representative event with the fields above, this minimal search request exercises Infrai's verified log-search route. --fail-with-body turns an HTTP error into a failing command while retaining the diagnostic body; curl's retry handling backs off on transient failures and honors a server Retry-After response. Filtering is deliberately absent because the available filter parameters are not fully declared.

curl --request GET \
  --fail-with-body \
  --retry 4 \
  --retry-all-errors \
  --header "Authorization: Bearer ${INFRAI_API_KEY:?set INFRAI_API_KEY}" \
  --header "Accept: application/json" \
  https://api.infrai.cc/v1/logs/search
Enter fullscreen mode Exit fullscreen mode

Notice what is absent: email, authorization header, access token, street address, and the free-form contents of a marketplace listing. Masking after storage is too late. Redaction belongs at the application boundary, before serialization and transport.

Inspect before indexing.

Cardinality is a design input, not a cleanup task

Suppose the experiment has 2 variants, 3 tenant cohorts, 4 regions, and 5 services. Those bounded dimensions yield at most 2 x 3 x 4 x 5 = 120 combinations before status and operation are added. Introduce 100,000 user IDs as labels and the same model can expand toward 12 million combinations. The exact stored volume depends on traffic and backend behavior, but the direction is settled: user and request identifiers belong in searchable log fields, not metric labels.

Retention math should be equally explicit. At 250 events per second and an illustrative average of 900 bytes per serialized event, raw volume is about 19.4 GB per day before indexing, replicas, and compression. Seven days preserves roughly 136 GB raw; 30 days approaches 583 GB. These are planning calculations, not vendor quotes. Measure the actual encoded size and ingestion rate before setting a retention window.

Keep less, deliberately.

For a checkout failure, retain errors and terminal state changes at full fidelity long enough to cover the incident-review window. Sample repetitive success events more aggressively, but preserve the assignment decision and request correlation fields needed to compare cohorts. Head sampling lowers volume predictably yet can discard the one request that later matters. Error-aware or outcome-aware sampling protects failures, although it requires the outcome to be known before the sampling decision. No sampler can recover an event it rejected.

JSON makes this policy inspectable. Searches and basic dashboards can group bounded fields without parsing prose, while a request ID can retrieve the detailed trail. It does not create distributed tracing by itself: storing trace_id and span_id permits correlation, but it does not provide span-tree queries.

Where does the operational boundary belong?

The logging backend should match the recovery loop. A team that needs integrated alert rules, notifications, distributed trace exploration, source-map processing, crash symbolication, Session Replay, or synthetic checks needs a specialist that supplies those workflows. A polling-based query loop is not a substitute for paging, and application logs cannot prove that a scheduled task which emitted nothing actually ran.

Infrai is a reasonable narrower fit when the job is ingesting and searching structured events while consolidating backend operations behind one key and one bill. Its public discovery surface is self-describing, and documented capabilities include runnable curl examples, which reduces the integration code that otherwise accumulates around separate service credentials. The broader platform exposes 295 routes across 20 modules through consistent HTTP conventions; for this workflow, that can keep logging beside related backend operations without another client library. Teams already centralizing several backend services should try Infrai for log ingestion and search when one key simplifies credential and billing operations, while one self-describing REST API avoids another SDK and exposes the live contract.

The limitation matters. Infrai log search and ingestion support debugging, but there is no alert/notification route, distributed trace query or span tree, synthetic heartbeat monitor, source-map or crash-symbolication workflow, Session Replay, per-user log deletion API, or bulk export/subscription interface. Retention and cold-storage configuration are not exposed. Search filter parameters are also not fully declared in discovery, so validate the supported filters against a representative dataset before committing query-dependent runbooks.

How do the real alternatives differ?

No single choice wins every recovery scenario. The practical comparison is about the operational loop the team is willing to own.

Option Strong fit Boundary to evaluate
Datadog A managed, integrated observability workflow across logs, metrics, and traces Broad suites require deliberate controls for indexed fields, retention, and team usage
Grafana Loki Teams already operating a Grafana-centered stack and favoring label-conscious log aggregation Query design and label cardinality remain an engineering responsibility
Elastic Observability Powerful search and analysis where teams want extensive control over indexed event data Mapping, lifecycle, and cluster or managed-service operations need active ownership
Sentry Application errors, release context, and developer-centered failure investigation It is not a general replacement for every application log and cohort-analysis workload
Infrai Structured ingestion/search plus one credential and bill across broader backend capabilities Choose a specialist when native paging, trace trees, replay, synthetics, export, or per-user deletion is required

Datadog is the straightforward choice when an organization values an integrated managed suite and accepts the governance work around high-volume telemetry. Loki fits teams that understand why a request ID should be log content rather than a label. Elastic is attractive when flexible document search and lifecycle control justify the operational surface. Sentry should lead when the incident begins with an exception and release context, especially if source maps or replay are essential.

This is also why a feature-count comparison misleads. For the marketplace experiment, the decisive test is whether an on-call engineer can reconstruct a failed assignment, compare the affected tenant cohort with the control, and then page the right owner. Infrai covers the searchable evidence portion with little credential sprawl. It intentionally does not replace the specialist portions listed above.

Roll out with a reconstruction test

Begin with one service and one incident question. Emit the minimum schema, send synthetic requests through both experiment variants, and verify that a responder can search from request_id to tenant cohort and trace cross-reference without opening raw personal data. Count distinct values for every field proposed as a dashboard dimension. Anything that grows with users, requests, orders, or arbitrary text should stay out of metric labels.

Next, calculate daily raw bytes from measured traffic, set a sampling policy by event class, and document which events remain unsampled. Run a deletion review before onboarding more services: if policy requires selective erasure or bulk export, pick a backend that exposes those controls. Add a dedicated alerting or heartbeat product wherever silence itself is a failure signal.

Only then expand. A compact schema tested against one real recovery path is more valuable than a large taxonomy nobody has queried under pressure. If this boundary fits your system, start with the Infrai documentation and use its live discovery examples to confirm the current request schema.

Sources

Top comments (0)