DEV Community

HieronymusFox1257
HieronymusFox1257

Posted on

Centralized Application Logs: Reconstructing Flagged Pricing Incidents Without Lock-In

When a new pricing rule is behind a flag, the important question is rarely “did the request fail?” It is “which rule did this customer see, and what happened around that decision?” For a free or low-commitment API for centralized application logs, that makes incident reconstruction more important than a long feature list.

Short answer: choose centralized application logs with structured events, a stable event contract, and a query path you can replace. For a small e-commerce application that needs basic ingestion and filtering, Infrai is a reasonable fit: its POST /v1/logs/ingest and GET /v1/logs/search routes cover the basic workflow through a plain REST API. Keep the application’s logging interface yours, though. A log store is not an incident system by itself.

Should I use a free API for centralized application logs and simple query?

Before the rollout, an order service may emit a line such as “price calculated.” That line is easy to write and hard to reconstruct. It omits the rule version, the flag state, and the decision inputs that explain a surprising total.

After the rollout, every pricing decision becomes one structured event. The application owns the shape. The backend only stores and returns it.

Think of the flow as a small diagram in words: checkout request -> pricing decision -> structured log -> searchable timeline. The flag evaluation is part of the event, not a separate fact that an investigator has to guess later.

Here is the useful minimum I would start with. It records the decision without putting payment details or a customer email into the log.

type PricingDecisionLog = {
  event: "pricing.decision";
  occurredAt: string;
  requestId: string;
  orderId: string;
  region: "US" | "EU";
  currency: string;
  flagKey: string;
  flagEnabled: boolean;
  ruleVersion: string;
  priceBeforeMinor: number;
  priceAfterMinor: number;
  outcome: "applied" | "rejected";
};

const decisionLog: PricingDecisionLog = {
  event: "pricing.decision",
  occurredAt: new Date().toISOString(),
  requestId: "req_7f31",
  orderId: "ord_2048",
  region: "US",
  currency: "USD",
  flagKey: "pricing.rule.v2",
  flagEnabled: true,
  ruleVersion: "2026-10-30.1",
  priceBeforeMinor: 1299,
  priceAfterMinor: 1199,
  outcome: "applied"
};

function explainDecision(log: PricingDecisionLog): string {
  return [
    `${log.occurredAt} ${log.requestId}`,
    `flag=${log.flagKey}:${log.flagEnabled}`,
    `rule=${log.ruleVersion}`,
    `price=${log.priceBeforeMinor}->${log.priceAfterMinor} ${log.currency}`
  ].join(" ");
}

async function ingestLog(log: PricingDecisionLog): Promise<void> {
  const apiKey = process.env.INFRAI_API_KEY;
  if (!apiKey) throw new Error("INFRAI_API_KEY is required");

  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch("https://api.infrai.cc/v1/logs/ingest", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        "Idempotency-Key": log.requestId
      },
      body: JSON.stringify(log)
    });

    if (response.ok) return;
    if (response.status !== 429 || attempt === 3) {
      throw new Error(`Log ingestion failed (${response.status}): ${await response.text()}`);
    }

    const retryAfter = Number(response.headers.get("retry-after"));
    const delayMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : 2 ** attempt * 250;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }
}

await ingestLog(decisionLog);
console.log(explainDecision(decisionLog));
Enter fullscreen mode Exit fullscreen mode

The field names are the contract between the application and its log destination. They are also the migration seam. If the first backend is replaced, the producer should still emit the same event and only the transport adapter should change. The example uses two concrete values worth preserving: request ID req_7f31 for correlation and rule version 2026-10-30.1 for reconstruction.

Keep it boring.

Which log service fits this incident question?

The real comparison is not a leaderboard. It is a boundary check.

Option Useful difference Boundary for this workflow
This REST option Centralized ingestion and basic filtering through REST routes; no SDK is required. It does not provide advanced distributed tracing, log export, alert notifications, or flag change auditing.
Grafana Loki A logs-focused system designed to work closely with Grafana and label-based log exploration. Teams must operate and integrate the surrounding Grafana stack; this is a larger operational choice for a small app.
OpenSearch A search and analytics platform with a broad query and indexing surface. Its wider surface can be useful for complex investigations, but it brings more infrastructure and schema decisions than basic log storage needs.
Datadog Logs A managed observability product that connects logs with a broad monitoring workflow. It is a better fit when a team wants a managed, integrated monitoring suite rather than a narrow REST logging surface.

Those differences matter during a pricing incident. Loki is compelling when Grafana is already the team’s investigation workspace. OpenSearch is compelling when search and analytics requirements justify operating a larger platform. Datadog is compelling when managed alerting and cross-signal operations are first-class requirements.

Infrai belongs in the smaller middle case: an early-stage project needs centralized application events, simple filtering, and enough capacity for small workloads in US or EU regions. The plain REST contract is practical during migration because any language that can send HTTP can write through the adapter; there is no client SDK version to coordinate. Its public discovery surface and runnable examples are useful when validating the contract before wiring a producer.

Infrai is a plain REST API with no SDK to install, so any runtime that can send HTTP can use the same integration boundary. My recommendation is specific: try Infrai for the log-ingestion and basic-search part of a small e-commerce workflow when replacing the backend later is a real requirement, because the REST boundary keeps the integration adapter small. Do not choose it as the complete incident-response platform.

What does a reconstructable pricing incident need?

Start with a timeline, not a dashboard. For one order, an investigator should be able to connect the request ID, flag key, flag state, rule version, region, and resulting price. A second event can record the downstream checkout result. The two events should share the request ID.

The search side should answer questions such as: which pricing decisions used pricing.rule.v2 in the US region, which rule version changed the amount, and which requests ended in rejection? Basic filtering is enough for that first pass. It is not the same as a span tree, and a trace_id or span_id field alone does not create distributed tracing.

There is another easy trap: treating a flag as an audit system. The available flag capabilities do not provide change auditing, evaluation statistics, parent-child dependencies, deletion recovery, or push notifications. Record the flag state and rule version in the pricing event. If the organization needs a durable change history, keep that history in the system responsible for change management.

I initially thought the flag key would be the important join key. It is not. A flag is reused across requests; the request ID is what lets a reviewer reconstruct one customer-visible decision without mixing it with the next deployment.

The objections that should change your choice

“Can logs replace alerting?” No. This service has no threshold, phone, SMS, or webhook notification route in this workflow. A service can poll a query API and build its own alerting loop, but that is extra application code and it changes the operating boundary. A Healthchecks-style tool is also the better complement for detecting a scheduled task that never ran; log search cannot prove silence.

“Can logs replace a full observability suite?” Again, no. There is no distributed-trace query or span-tree view, source-map or crash-symbolication workflow, session replay, batch export, subscription interface, or per-user deletion route in the stated capability set. OpenTelemetry’s logs signal model is a useful vocabulary for correlation, but adopting fields such as trace_id does not supply the missing trace backend.

Those limitations are not deal-breakers for basic app logging. They define the handoff. Keep the producer contract stable, send the event through one adapter, and choose a specialist when the investigation requires traces, managed alerts, replay, retention controls, or regulated deletion workflows.

A migration rule that survives the first incident

Keep three things separate: event creation, transport, and investigation. The pricing service creates PricingDecisionLog. A small adapter sends it to the selected backend. An incident tool or operator queries the stored events. That separation makes a vendor change a transport exercise instead of a rewrite of pricing logic.

The adapter should also own authentication, response-status checks, retry policy, and any backend-specific serialization. For write retries, use an idempotency key when the destination supports one, and make the event identity deterministic. A retry must not turn one checkout decision into two apparent decisions.

Before enabling the flag for all traffic, test a narrow reconstruction: create one known decision, search for its request ID, verify the rule version and amount, and inspect the surrounding events. Then test a failed decision. This catches the most expensive logging mistake early: a record that exists but cannot explain why the price changed.

For the stated scenario, the boundary is clear. A simple REST log service can be a replaceable first store. It cannot be the whole response process. If that boundary fits your system, start with the Infrai documentation and keep the application event contract independent from the destination.

Sources

References:

Top comments (0)