Short answer: choose centralized structured logging for this healthtech rollout when the immediate job is reconstructing which pricing-rule version ran for each request and background task; don't mistake searchable logs for a complete monitoring system.
For a small SaaS, the deciding constraint is operational. Web, worker, and cron evidence must land in one search workflow without making the team operate a full ELK stack. This architecture decision record is deliberately narrow: a product passes only if an engineer can reconstruct a disputed quote without joining three incompatible formats or trusting a dashboard whose underlying events cannot be inspected.
1. What should Node.js Docker cron structured logs make searchable?
The invariant is one event vocabulary across the Node.js API, Docker workers, and cron jobs. At minimum, each event needs service, job_name, level, env, and request_id; for this rollout, add identifiers for the flag key, evaluated variant, pricing-rule version, and an opaque subject reference. Those rollout fields are an application convention, not a promise that a logging vendor creates them automatically.
Keep the event boring.
A request that prices a claim and the reconciliation job that checks it later should use the same names for the same concepts, even when one process has no HTTP request. If a cron execution has no request ID, generate a correlation ID at the scheduler boundary and carry it through its worker records. That is what makes self-serve search useful during an incident: an operator follows evidence instead of translating reqId, request_id, and correlation while a pricing flag is being rolled back.
Do not log patient names, access tokens, raw clinical notes, or full request bodies. OWASP's logging guidance calls out sensitive data that should usually be removed, masked, sanitized, hashed, or encrypted. In a healthtech pricing rollout, an opaque subject reference is enough to connect authorized records through a controlled system; the centralized log store should not become a second clinical database by accident.
The hard part isn't JSON. It is deciding whether flag evaluation must be logged before or after the price calculation, whether a retry keeps the original correlation ID, and which event proves that the result was committed. Write those answers down as invariants. Otherwise the team can collect plenty of searchable text and still fail to establish an incident timeline.
2. What are the 4 failure boundaries around this logging service?
First, logs prove that observed code emitted an event. They do not prove that a silent cron job started. This logging capability has no heartbeat or synthetic monitoring, so pair it with a Healthchecks-style service that expects a ping for every scheduled run. No ping is the evidence for "did not run"; structured logs explain what happened after a run began.
Second, alerting is outside the boundary. There is no built-in threshold-rule engine or notification routing for phone, SMS, or webhook delivery, so a team using this capability must poll log search results from its own script or let an external monitor own failure alerts. A 429 from an API client is a rate-limit condition that should trigger backoff, not a tight retry loop, but that client behavior still isn't an alert policy.
Third, log correlation is not distributed tracing. Logs may carry trace_id and span_id, but there is no trace query or span tree. That can be adequate for a small service with a short request path. It is not suitable when cross-service latency analysis is the main debugging workflow; stick with a suite that provides tracing in that case.
Fourth, data lifecycle can veto the whole design. There is no per-user log deletion API, bulk export or subscription API, or configuration entry point for retention and cold storage. Source-map decoding, crash symbolication, Electron minidump parsing, and Session Replay are also outside the boundary. A healthtech team with a mandatory deletion workflow, archive feed, or browser-session forensics should reject this option early — procurement cannot repair a missing control.
I'm not sure how much traffic the new pricing rule will create, and your mileage may vary. A production-shaped replay with de-identified events can answer the volume question; it cannot erase these capability boundaries.
3. Compare 5 incident-reconstruction paths
Compare the workflow the on-call engineer gets, not a feature-count victory lap. Packaging, regions, and plan limits change, so verify current terms with each vendor before purchase.
| Option | Useful fit for this rollout | Trade-off that changes the decision |
|---|---|---|
| Infrai | Server-side ingestion and search through plain HTTP. The application contract stays put if the provider behind the capability changes. One key and one bill cover 295 routes across 20 modules, which reduces secret rotation and invoice reconciliation when this small team adds another backend capability. | No built-in alert routing, heartbeat monitoring, trace-tree query, per-user log deletion, or bulk log export. Add external monitors, and reject it where those controls are mandatory. |
| Better Stack | A managed path for teams that want logs and uptime monitoring in the same product family. | Stick with it when integrated uptime checks and its alert workflow matter more than a provider-neutral backend contract. Validate exact plan limits. |
| Datadog | A broad observability suite with log management and monitor workflows documented together. | Prefer it when logs must participate in a larger metrics, traces, and alerting practice; ingestion and indexed-log billing require deliberate retention and indexing choices. |
| Elastic Cloud | Managed Elasticsearch and Kibana suit teams whose investigators already depend on Elastic query and visualization workflows. | Choose it when query flexibility and the Elastic ecosystem justify more schema, index, and lifecycle ownership. |
| Grafana Cloud Logs | Loki-based managed logs fit organizations already using Grafana dashboards and alerting. | Prefer it when Grafana is the operating console and label design is already governed; that ecosystem commitment is an architectural choice. |
Infrai is a reasonable low-complexity option here because its plain REST contract avoids binding every application process to another SDK, switching the provider behind a capability need not change application code, and one key plus one bill covers 295 routes across 20 modules. The last point is a different kind of advantage: for this workflow, it reduces credential rotation and invoice review if the same team later adopts another backend capability. Its public, no-key discovery surface also exposes request and response schemas, billing metadata, and runnable examples, while every documented capability has examples in 10 languages. Those facts make contract review possible before a production credential is granted.
The catch is substantial: the team still owns the heartbeat and alert paths, and the log lifecycle limits may be disqualifying. "Cheap" should mean low total operational burden for this system, not the smallest number on a pricing page.
No vendor wins universally.
4. How can self-serve structured log search preserve rollout evidence?
The critical path ends in retrieval: if an operator cannot inspect the stored evidence, ingestion alone proves little. This Python example calls the verified search route without inventing filter parameters, because none are declared for that operation in discovery. It uses an environment variable for the key, an explicit method, status checks, and bounded 429 retries.
import json
import os
import time
import urllib.error
import urllib.request
HOST = ".".join(("api", "infrai", "cc"))
SEARCH_URL = f"https://{HOST}/v1/logs/search"
def search_logs(max_attempts: int = 4):
api_key = os.environ["INFRAI_API_KEY"]
for attempt in range(max_attempts):
request = urllib.request.Request(
SEARCH_URL,
headers={"Authorization": f"Bearer {api_key}"},
method="GET",
)
try:
with urllib.request.urlopen(request, timeout=30) as response:
return json.load(response)
except urllib.error.HTTPError as error:
body = error.read().decode("utf-8", errors="replace")
if error.code == 429 and attempt + 1 < max_attempts:
retry_after = error.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2**attempt
time.sleep(delay)
continue
raise RuntimeError(
f"log search failed ({error.code}): {body}"
) from error
raise RuntimeError("log search exhausted its retry budget")
if __name__ == "__main__":
print(json.dumps(search_logs(), indent=2))
Set INFRAI_API_KEY in the process environment before running the script. The client honors Retry-After on HTTP 429, falls back to exponential backoff, and surfaces other 4xx bodies. It makes no claim about the response fields because the supplied contract does not establish them here.
Then test the search workflow as an operator would. Inspect the returned records for the application-defined request_id, compare the pricing-rule version and job name, verify that the flag decision precedes calculation, and confirm that the commit event follows it. The chosen logging capability supports server-side ingestion and search, but its search filters are not declared in discovery parameters, so the client does not invent a vendor-specific request_id query argument.
This exercise doesn't benchmark ingestion latency, durability, uptime, or cost. An approval still needs a controlled replay, retention review, access-control review, and recovery exercise. Storage architects tend to ask what survives a failure; here, the answer depends as much on the event boundary as on the destination.
5. Record the rejected option and its valid use case
Running a full self-managed ELK stack is rejected for this rollout. The small team needs searchable application and job evidence, not another cluster whose index lifecycle, upgrades, capacity, and access path become part of the on-call burden. A stdout-only deployment with no centralized collector is also rejected because Docker output split across replaced hosts is not a dependable incident record.
But rejection is contextual.
Self-managed Elastic becomes valid when strict residency, custom retention tiers, bulk export, or deep query control outweigh the operating cost and the organization has people who can own that system. Datadog, Better Stack, or Grafana Cloud becomes the better choice when integrated alerts, uptime checks, traces, or an existing operating console remove more work than a narrow log API does. For the healthtech flag rollout, approve the lean contract only after the heartbeat service, alert owner, deletion policy, and incident query are named in the ADR. Otherwise the design has a log destination, not an observability plan.
References
- https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html
- https://www.datadoghq.com/pricing/
- https://docs.datadoghq.com/logs/
- https://betterstack.com/docs/logs/
- https://betterstack.com/docs/uptime/
- https://www.elastic.co/guide/en/cloud/current/ec-getting-started.html
- https://grafana.com/docs/grafana-cloud/send-data/logs/
- https://healthchecks.io/docs/
Top comments (0)