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.
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.
1import { RouteHandler } from "gadget-server";23const route: RouteHandler = async ({ request, reply, logger }) => {4 logger.info({ ip: request.ip }, "requester ip");5 // log entry will show up in the Logs6 await reply.code(200).send("route response");7};
1import { RouteHandler } from "gadget-server";23const route: RouteHandler = async ({ request, reply, logger }) => {4 logger.info({ ip: request.ip }, "requester ip");5 // log entry will show up in the Logs6 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.
1export const run: ActionRun = async ({ logger }) => {2 const result = await someAPICall();34 // works but is discouraged, will log just this message to your logger5 console.log(`finished API call: ${result.statusCode}`);67 // works well and is encouraged, will log the message with surrounding context like the action and model, and is more performant8 logger.info({ statusCode: result.statusCode }, "finished API call");9};
1export const run: ActionRun = async ({ logger }) => {2 const result = await someAPICall();34 // works but is discouraged, will log just this message to your logger5 console.log(`finished API call: ${result.statusCode}`);67 // works well and is encouraged, will log the message with surrounding context like the action and model, and is more performant8 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.
1// log a plain old string message at the info level2logger.info("Hello world");34// log a structured object and a message at the info level5logger.info({ key: "value", foo: 42, record: someRecord }, "making progress");67// log an error at the error level8try {9 "foo" in null;10} catch (error) {11 logger.error({ error });12}
1// log a plain old string message at the info level2logger.info("Hello world");34// log a structured object and a message at the info level5logger.info({ key: "value", foo: 42, record: someRecord }, "making progress");67// log an error at the error level8try {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.
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:
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:
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.
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:
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.