Investigate application errors

A checkout release starts producing payment errors. Is every payment method affected, or should you investigate one gateway?

Keep a checkout simulator running while it sends structured application logs through the JavaScript SDK. Use Console to compare the arriving requests, locate the affected gateway, and inspect a failure. The repeating pattern is timeouts in release 1.9 credit-card payments, with bank transfers unaffected.

Allow about 15 minutes after workspace setup. Each cycle simulates 12 payment attempts, and cycles continue until you press Ctrl+C. The program does not contact a payment provider or use customer data.

1. Connect the sample application

You need a ready ScopeDB Cloud workspace, Node.js 20 or later, Git, and a terminal (macOS, Linux, or WSL). Use a test workspace: the program creates guide_logs.requests and continuously writes logs in the default scopedb database.

In your workspace, open Connect and copy the address labeled ScopeDB API.

Light-mode Console Connect page for Northstar Analytics, signed in as riverfox@example.com, with the example API address ap-southeast-1.aws.scopedb.cloud.
Copy the ScopeDB API address from your own workspace, then choose Create API key. The account and address shown are demonstration values.

Choose Create API key, give it a name such as event-tutorial, and choose an expiration. Select Create Key and copy the one-time secret. The connection walkthrough covers setup in more detail.

Light-mode Create API Key dialog with the name event-tutorial and a 90-day expiration.
Give the key a recognizable name, choose an expiration, and select Create Key. Save the secret when it appears.

Set the address and secret in the terminal where you will run the application:

export SCOPEDB_ENDPOINT="https://<endpoint>"
export SCOPEDB_API_KEY="<api-key>"

Keep the key in your local environment, outside source control. The program runs on your machine; your Console browser sign-in does not authenticate it.

Clone the Demo Scene repository and enter the application-log example. The project pins the JavaScript SDK to scopedb 0.3.0; npm ci installs its locked dependencies.

git clone https://github.com/scopedb/demo-scene.git scopedb-demos
cd scopedb-demos/application-logs
npm ci

If you already cloned the repository for another tutorial, enter its application-logs directory instead. The program, dependency lockfile, tests, and saved queries live together there.

2. Send structured request logs

Before producing logs, the simulator creates the guide_logs schema and this table if they do not exist. You do not need to run this definition separately:

CREATE TABLE guide_logs.requests (
    time timestamp,
    service string,
    level string,
    message string,
    var object
)

In each cycle, the simulator runs three attempts for each combination of release (1.8, 1.9) and gateway (credit_card, bank_transfer). Its payment function raises a timeout for two credit-card attempts in release 1.9. A request handler records both successful and failed attempts:

const log = {
  time: new Date().toISOString(), service: "checkout", level, message,
  var: { run_id: runId, request_id: requestId, release, gateway },
};
console.log(JSON.stringify(log));
await stream.send(log);

Each request produces one log. time, service, level, and message have typed columns; request context stays together in the var object. The same pattern fits a server request handler: keep the fields you need to compare alongside the message that explains the failure.

Start the producer with a label for this investigation, and leave this terminal running while you use Console:

SCOPEDB_DEMO_RUN=checkout-first npm start

The program prints the run ID and keeps producing logs. Each cycle has ten successful requests and two credit-card timeouts. The SDK dispatches buffered logs within about one second; send() alone is not a delivery acknowledgement.

Open Console in your browser while the producer continues. After viewing a result, select Run again to include newly committed requests. Console Query shows the result of each execution, rather than automatically updating the previous result.

When you finish the walkthrough, press Ctrl+C. The program stops generating requests, waits for stream.shutdown(), and reports how many logs committed. If delivery fails, inspect the run before retrying. Use a fresh run ID when restarting this exercise so earlier requests remain separate.

For a fixed verification run, use SCOPEDB_DEMO_RUN=checkout-check npm start -- --cycles 1. That explicitly stops after 12 requests; the default producer keeps running.

3. Compare releases and gateways

Open Query in the same Console workspace. Run each ScopeQL block separately using Run. First, verify the complete request population:

FROM guide_logs.requests
WHERE var['run_id']::string = 'checkout-first'
AGGREGATE count() AS requests,
    sum(if(level = 'error', 1, 0)) AS errors

After the first complete cycle has committed, expect at least 12 requests and two errors. Select Run again after a few seconds: both counts should grow. The screenshot shows one completed cycle. Successful requests matter: counting only error logs cannot tell you the failure rate.

Light-mode Console Query editor and result showing 12 checkout requests and two errors.
Example after one completed cycle: 12 requests and two errors. Keep the producer running and run the query again to see these counts grow.

Select Run again while the producer stays active. The same query includes newly committed requests:

Light-mode Console result showing a later execution with 1140 requests and 190 errors while the producer continues.
A later execution of the same query includes new requests. Your totals depend on how long the producer has been running.

Compare each release and gateway:

FROM guide_logs.requests
WHERE var['run_id']::string = 'checkout-first'
SELECT level, var['release']::string AS release,
    var['gateway']::string AS gateway
GROUP BY release, gateway
AGGREGATE count() AS requests,
    sum(if(level = 'error', 1, 0)) AS errors
SELECT *, 100.0 * errors / requests AS error_percent
ORDER BY release, gateway
releasegatewayrequestserrorserror_percent
1.8bank_transfer300
1.8credit_card300
1.9bank_transfer300
1.9credit_card3266.67
Light-mode Console query result with four release and gateway groups; only release 1.9 credit-card payments have errors.
One-cycle snapshot: three requests per group, with two errors only in release 1.9 credit_card. Counts grow as the producer continues.

The table and screenshot show one completed cycle; your counts continue increasing. Each complete cycle gives both releases the same number of attempts per gateway. A query during a partially delivered cycle may show temporarily uneven counts. The last percentage is rounded here. The regression stays concentrated in credit-card requests after the release. This gives you a specific next check: the credit-card integration or configuration change in 1.9. It does not establish the cause of a real production incident.

4. Inspect a failed request

Return the error messages with their request identifiers:

FROM guide_logs.requests
WHERE var['run_id']::string = 'checkout-first' AND level = 'error'
SELECT time, message, var['request_id']::string AS request_id, var
ORDER BY time DESC, request_id
LIMIT 10

The first cycle produces req-08 and req-09, both with Credit card gateway timed out; later cycles produce new request IDs. This query shows up to ten recent failures. Double-click a var cell to inspect the release and gateway together in Cell details. Copy a request ID to find the corresponding request in your application's other logs or traces.

Light-mode Console Cell details showing req-08, release 1.9, the credit_card gateway, and run checkout-first.
Double-click an error row’s var cell. This early snapshot shows req-08; later cycles have different request IDs.

For a visual investigation, open Explore and select the guide_logs schema and requests table. Set the time range to include when you ran the simulator, then enter this WHERE expression:

var['run_id']::string = 'checkout-first' AND level = 'error'
Light-mode Explore with the requests table, run and error filters, a time histogram, and two matching timeout records.
Select the requests table, enter the WHERE expression, and select Run. This first-cycle snapshot has two errors; run again to include new arrivals.

Click either record to open Record details and read its nested fields. If it shows no records, clear or widen the time range: the run ID identifies this exercise, but Explore also applies its selected time window. The Explore walkthrough illustrates the record controls.

You now have an investigation you can repeat as events arrive: a growing request population, errors concentrated in one release/gateway group, and the request IDs and payloads behind them.

5. Apply the workflow to your application

Replace the simulator's payment function with your request handler and keep one outcome log per attempt. Include successful requests when calculating failure rates. Attach request IDs, release versions, and the business dimensions you need to compare; omit credentials and payment details from the payload.

Use a shared SDK client and append stream in your server process. Flush at shutdown and handle delivery failures using the JavaScript SDK guide. The simulator creates its schema at startup to make the demo self-contained. For an application, create and manage table definitions ahead of deployment using Console or the CLI catalog workflow, and keep request handling focused on writes. Integrate buffering and shutdown with your server's lifecycle.

Continue with Model event data when fields evolve, Query events for more investigation patterns, or Set data retention to choose how long to keep these logs.

Troubleshooting

SymptomNext step
401 or 403Check the API key, its expiration, the workspace, and the API address copied from Connect.
Run ID already has logsQuery that run first. For a fresh exercise, use a new SCOPEDB_DEMO_RUN and change every query filter to match.
No rows in QueryCheck the selected workspace, schema, run ID, and whether the program printed the commit confirmation.
No records in ExploreCheck the table and WHERE expression, then widen the selected time range to include the run.
Timeout or failed appendQuery the run's count before retrying; a lost response can follow a successful write. Use a new run ID for a fresh sample.