Logging

Gadget has a built in, scalable logging system available for use in every application. Gadget emits logs about important happenings within your application by default, like incoming HTTP requests, webhooks, and connection syncs, and you can emit your own logs. Logs can be viewed and searched in Gadget's built in Log Viewer.

Logging from actions

Every effect on an Action will be passed a logger object in the incoming effect arguments. The logger conforms to the logger API and will output log statements viewable in Gadget's Log Viewer.

some-model/run/effect.js
JavaScript
1module.exports = async ({ api, record, logger }) => {
2 const newRecord = await api.otherModel.create({
3 otherModel: { title: "new title" },
4 });
5 logger.info({ result: newRecord }, "created otherModel record");
6 // log entry will show up in the Log Viewer
7};

Logging from HTTP routes

Every HTTP route will be passed a request.logger object on the incoming request object which can be used during the request to write logs. The logger conforms to the logger API and will output log statements viewable in Gadget's Log Viewer.

routes/GET-example.js
JavaScript
module.exports = async (request, reply) => {
logger.info({ ip: request.ip }, "requester ip");
// log entry will show up in the Log Viewer
await reply.code(200).send("route response");
};

console.log

Gadget supports console.log statements for logging in your application and will show console.log messages in the log viewer at the info log level.

some-model/run/effect.js
JavaScript
1module.exports = async ({ api, record, logger }) => {
2 const result = await someAPICall();
3
4 // works but is discouraged, will just log the message to the log viewer
5 console.log(`finished API call: ${result.statusCode}`);
6
7 // works well and is encouraged, will log the message with surrounding context like the action and model, and is more performant
8 logger.info({ statusCode: result.statusCode }, "finished API call");
9};

Using the structured logger object is recommended over using console.log for better performance, and to allow Gadget to add more context to your log messages.

logger API

Log messages are sent to the logger by calling one of the logging methods with an optional structured data object, and a string message.

JavaScript
1// log a plain old string message at the info level
2logger.info("Hello world");
3
4// log a structured object and a message at the info level
5logger.info({ key: "value", foo: 42, record: someRecord }, "making progress");
6
7// log an error at the error level
8try {
9 "foo" in null;
10} catch (error) {
11 logger.error({ error });
12}

The default log levels (and associated log functions) are trace, debug, info, warn, error, and fatal.

The passed logger object is an instance of a pino.Logger managed by the Gadget platform. View the Pino logger docs for full API documentation.

Passing structured data

An object can optionally be supplied as the first parameter to log messages. Each key and value of the object is stringified and written to your application's logs as JSON.

JavaScript
logger.info({ value: { foo: "bar" } });

Log calls at any level can also pass Error objects to get an easy-to-digest log entry in the Log Viewer for the error including it's stack trace and any other associated details. To log Error objects, pass the object at the error key of the structured data:

JavaScript
const error = new Error("something went wrong");
logger.warn({ error: error }, "error encountered, continuing");

Viewing Logs

Gadget logs just about everything that happens in your application to the built in Log Viewer. Logs can be queried using LogQL, a powerful query language for searching through logs.

Gadget apps use structured logging, which means logs are JSON objects that can include a string message and also key value pairs of other data. console.log works like it might in the browser or node.js in other JavaScript environments, but if you want to log structured data, you can use the structured logger object passed to your effect files, or request.log passed to HTTP routes.

Querying for a string

If you want to search your logs for a specific string, use the |= operator:

logql
{level="info|warn|error", environment_id="{YOUR_ENVIRONMENT_ID"} |= "some string"

Gadget will return all logs containing this string over the searched time range.

Excluding a string

If you want to search your logs, but exclude a specific string, use the != operator:

logql
{level="info|warn|error", environment_id="{YOUR_ENVIRONMENT_ID"} != "some string"

Querying for key/value pairs

If you want to search your logs for structured entries where a particular key has been set to a particular value, add a | json filter, and then use the | operator to search for a particular key value pair.

logql
{level="info|warn|error", environment_id="{YOUR_ENVIRONMENT_ID"} | json | someKey="some value"

Key/value search parameters must be preceded by the | json | operator to parse all the keys and values in the structured logs.

Multiple keys and values can be searched for simultaneously with the and and or operators

logql
{level="info|warn|error", environment_id="{YOUR_ENVIRONMENT_ID"} | json | someKey="some value" or someKey="some other value"
# or
{level="info|warn|error", environment_id="{YOUR_ENVIRONMENT_ID"} | json | someKey="some value" and anotherKey="some other value"

Stream selectors

Gadget logs are queryable via LogQL, which always requires a stream selector within the log query. The stream selector is the part of the query at the beginning that looks like {level="info|warn|error"}. Gadget includes a default stream selector in all generated log queries. You can modify the stream selector, but you can't omit it. If you need to restore the default stream selector in your query, click the Reset button in the query bar.

Platform Logs

Gadget emits log entries as a platform in order to keep you informed about what work Gadget is conducting within your application. Gadget emits log entries for:

  • incoming requests and responses for HTTP routes
  • incoming requests and responses for GraphQL API requests
  • incoming webhooks for Connections including the Shopify Connection
  • sync executions for the Shopify Connection
  • Shopify connection shop installation and uninstallation