Skip to content

Dynamic Tags

import { Aside } from ‘@astrojs/starlight/components’;

Tags are the most powerful filtering mechanism in gunsole. They’re simple key-value pairs attached to log entries, but the magic is in how they surface in the UI.

  1. You send a log with tags
  2. The desktop app extracts each key-value pair
  3. New keys appear as filter dropdowns in the filter bar
  4. New values appear as options in those dropdowns
  5. Value counts are shown so you know the distribution

No schema. No config file. No “register your tags” step. Just send them.

gun.info({
bucket: "api",
message: "Request handled",
tags: {
route: "/users",
method: "GET",
status: "200",
},
});
gun.info({
bucket: "api",
message: "Request handled",
tags: {
route: "/orders",
method: "POST",
status: "201",
},
});
gun.error({
bucket: "api",
message: "Request failed",
tags: {
route: "/orders",
method: "POST",
status: "500",
},
});

After these three logs, the filter bar shows:

  • route dropdown: /users (1), /orders (2)
  • method dropdown: GET (1), POST (2)
  • status dropdown: 200 (1), 201 (1), 500 (1)

Select status: 500 to see only the failed request. Select route: /orders + method: POST to see all POST requests to /orders.

Both tags and context accept arbitrary data, but they serve different purposes:

TagsContext
TypeRecord<string, string> (strings only)Record<string, unknown> (any JSON)
PurposeFiltering and groupingDetailed inspection
UIAppear as filter dropdownsShown in expanded log view
IndexedYes (fast queries)No (stored as JSON blob)

Rule of thumb: If you want to filter by it, use a tag. If you want to inspect it when looking at a specific log, use context.

gun.info({
bucket: "api",
message: "POST /orders → 201",
// Things you filter by
tags: {
route: "/orders",
method: "POST",
status: "201",
},
// Things you look at when investigating
context: {
orderId: "ord_abc123",
items: 3,
total: 149.99,
userId: "u_456",
latencyMs: 234,
},
});

Set defaultTags in your client config to automatically attach tags to every log:

const gun = createGunsoleClient({
projectId: "my-app",
apiKey: "dev",
mode: "local",
defaultTags: {
service: "api-server",
region: "us-east-1",
version: "1.4.2",
},
});

Every log from this client will have service, region, and version tags. Per-log tags are merged on top — if there’s a conflict, the per-log value wins.

TagExample valuesUse case
route/users, /ordersFilter by API endpoint
methodGET, POST, PUTFilter by HTTP method
status200, 404, 500Filter by response status
featureauth, checkout, searchFilter by product area
actionclick, submit, navigateFilter by user action type
serviceapi, worker, schedulerFilter by microservice
envproduction, staging, localFilter by environment
componentHeader, Cart, ModalFilter by React component

Tags are stored in a separate tags table with a many-to-many relationship to logs. This makes tag queries fast — they use indexed JOINs rather than scanning JSON blobs.