Logger 

Logging from actions 

Every action will be passed a logger object in the incoming arguments. The logger conforms to the logger API and will output log statements viewable in Gadget's Logs.

api/actions/someAction.js
JavaScript
export const run: ActionRun = async ({ logger, api }) => {
const newRecord = await api.otherModel.create({ title: "new title" });
logger.info({ result: newRecord }, "created otherModel record");
// log entry will show up in the Logs
};
export const run: ActionRun = async ({ logger, api }) => {
const newRecord = await api.otherModel.create({ title: "new title" });
logger.info({ result: newRecord }, "created otherModel record");
// log entry will show up in the Logs
};

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 adheres to the logger API and displays log statements in Gadget's Logs.

api/routes/GET-example.js
JavaScript
1import { RouteHandler } from "gadget-server";
2
3const route: RouteHandler = async ({ request, reply, logger }) => {
4 logger.info({ ip: request.ip }, "requester ip");
5 // log entry will show up in the Logs
6 await reply.code(200).send("route response");
7};
1import { RouteHandler } from "gadget-server";
2
3const route: RouteHandler = async ({ request, reply, logger }) => {
4 logger.info({ ip: request.ip }, "requester ip");
5 // log entry will show up in the Logs
6 await reply.code(200).send("route response");
7};

console.log 

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

api/models/todo/actions/apiCall.js
JavaScript
1export const run: ActionRun = async ({ logger }) => {
2 const result = await someAPICall();
3
4 // works but is discouraged, will log just this message to your logger
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};
1export const run: ActionRun = async ({ logger }) => {
2 const result = await someAPICall();
3
4 // works but is discouraged, will log just this message to your logger
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}
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" } });
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 encountered, continuing");
const error = new Error("something went wrong");
logger.warn({ error }, "error encountered, continuing");

Viewing logs 

Gadget logs all events in your application. Logs are queryable using LogQL, a powerful language for searching through logs.

Gadget apps utilize structured logging, where logs are JSON objects that can contain 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 action files or request.log passed to HTTP routes.

Querying for a string 

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 

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 

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.

You can search for multiple keys and values simultaneously using 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"

Querying for a particular trace_id 

To search your logs for a specific trace_id (see Trace IDs), use the json filter and then filter on the trace_id.

logql
{level="info|warn|error", environment_id="{YOUR_ENVIRONMENT_ID"} | json | trace_id="abcde12345"

Gadget will return all logs tagged with this trace ID over the searched time range. If nothing is found, you may need to expand your time ranger further to find when a particular trace was executed.

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

Trace IDs 

Gadget attaches a unique identifier to request called a trace ID. Every log emitted by any piece of code during that request will have the same trace ID marked on it, which allows you to find all the logs for one given request in your Logger.

Viewing all the logs for a single trace 

To view the logs for one particular request through your application, and filter out all other requests, click the blue trace_id label in the logger for any log entry:

A screenshot of the Logger and where to click to filter logs by trace ID

When you click a trace ID, the logger will adjust the log search query to filter out everything except logs for this particular trace. You can then use the LogQL query language to filter the logs further.

Getting the trace ID for a request 

Gadget replies to each request with the trace ID in the x-trace-id header. If you use your browser's Developer Tools and look in the Network tab at a request made to the Gadget platform, you can find an x-trace-id response header showing the trace ID that Gadget assigned that request.

A screenshot of the Chrome DevTools showing the x-trace-id response header

For requests to your app's GraphQL API, Gadget will also include an extensions property in the JSON response containing the trace_id for easy copy/pasting, as well as a direct link to the logs within your app for that trace. Requests made from any client will include this data, including your app's frontend and the Gadget GraphQL Playground. For example, this request made in the playground includes a link to the logger:

A screenshot of the Gadget GraphQL Playground showing the logs link

The trace ID and logs link are not sensitive data and are sent with every response. Only developers with access to your application's editor will be able to view the logs for a given trace ID.

Was this page helpful?