> ## Documentation Index
> Fetch the complete documentation index at: https://namespace.so/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Read logs and egress records

> Stream and fetch instance logs, and read egress records, with the TypeScript SDK.

`client.observability` reads what an instance produced. Stream logs while an instance runs, fetch logs for a time range after the fact, or read the DNS egress records collected for an instance.

Resource metrics and instance state notifications are served by the Compute service rather than the Observability service, so they are called on `client.compute`. They are documented here because they answer the same kind of question. See [Compute observability](/docs/architecture/compute/observability) for what the platform collects.

The examples on this page use a Compute client:

```typescript theme={null}
import { createComputeClient } from "@namespacelabs/sdk/api/compute";

const client = createComputeClient();
```

## `observability.streamInstanceLogs()`

Stream the logs of one instance. With `follow` set, the call waits for additional logs and terminates when the instance shuts down.

<h3 id="observability-streaminstancelogs-example">
  Example
</h3>

```typescript {1-4} theme={null}
for await (const block of client.observability.streamInstanceLogs({
  instanceId,
  follow: true,
})) {
  for (const line of block.lines) {
    console.log(line.stream, line.content);
  }
}
```

<h3 id="observability-streaminstancelogs-api-reference">
  API reference
</h3>

```typescript theme={null}
streamInstanceLogs(
  request: MessageInitShape<typeof StreamInstanceLogsRequestSchema>,
  options?: CallOptions,
): AsyncIterable<LogBlock>
```

<h4 id="observability-streaminstancelogs-arguments-and-options">
  Arguments and options
</h4>

<ResponseField name="instanceId" type="string" required>
  The instance to stream logs from.
</ResponseField>

<ResponseField name="follow" type="boolean">
  When `true`, keeps the stream open and waits for new logs. The stream terminates when the instance shuts down.
</ResponseField>

<ResponseField name="matchContainerNames" type="StringMatcher">
  Only stream logs from matching containers.
</ResponseField>

<ResponseField name="options" type="CallOptions">
  Cancellation, timeout, and header options. See [Shared call options](/docs/reference/typescript-sdk/compute/manage#shared-call-options).
</ResponseField>

<h4 id="observability-streaminstancelogs-return-value">
  Return value
</h4>

Returns an async iterable of `LogBlock`. Each block batches lines from the same container to reduce bytes on the wire, so blocks from different containers can interleave.

<ResponseField name="labels" type="Record<string, string>">
  Labels shared by every line in the block, such as the container the lines came from.
</ResponseField>

<ResponseField name="lines" type="LogBlock_Line[]">
  The batched lines. Each has a `timestamp`, the line `content`, and the `stream` it came from, such as `stdout` or `stderr`.
</ResponseField>

Omitting the instance ID fails with `InvalidArgument`. An unknown instance ID fails with `NotFound`.

***

## `observability.fetchInstanceLogs()`

Fetch logs for one or more instances over a time range. The instances can still be running.

<h3 id="observability-fetchinstancelogs-example">
  Example
</h3>

```typescript {4-12} theme={null}
import { timestampFromDate } from "@bufbuild/protobuf/wkt";
import { StringMatcher_Operator } from "@namespacelabs/sdk/proto/namespace/stdlib/matchers_pb";

const page = await client.observability.fetchInstanceLogs({
  matchInstanceIds: {
    values: [instanceId],
    op: StringMatcher_Operator.IS_ANY_OF,
  },
  timestampRange: {
    after: timestampFromDate(new Date(Date.now() - 30 * 60 * 1000)),
    before: timestampFromDate(new Date()),
  },
  linesPerPage: 500,
});

for (const line of page.logLine) {
  console.log(line.timestamp, line.source, line.content);
}
```

<h3 id="observability-fetchinstancelogs-api-reference">
  API reference
</h3>

```typescript theme={null}
fetchInstanceLogs(
  request: MessageInitShape<typeof FetchInstanceLogsRequestSchema>,
  options?: CallOptions,
): Promise<FetchInstanceLogsResponse>
```

<h4 id="observability-fetchinstancelogs-arguments-and-options">
  Arguments and options
</h4>

<ResponseField name="matchInstanceIds" type="StringMatcher">
  The instances to fetch logs for.
</ResponseField>

<ResponseField name="timestampRange" type="TimestampRange">
  The time range to read, as `after` and `before` timestamps.
</ResponseField>

<ResponseField name="matchContainerNames" type="StringMatcher">
  Only return logs from matching containers.
</ResponseField>

<ResponseField name="linesPerPage" type="number">
  The maximum number of lines to return per page.
</ResponseField>

<ResponseField name="paginationCursor" type="Uint8Array">
  The `paginationCursor` from a previous response, to read the next page.
</ResponseField>

<ResponseField name="options" type="CallOptions">
  Cancellation, timeout, and header options. See [Shared call options](/docs/reference/typescript-sdk/compute/manage#shared-call-options).
</ResponseField>

<Warning>
  A time range is required. Either query a naturally bounded range, such as the last 30 minutes, or look up when the instances ran and pass that range. Without one, the serving cost may be too high and the API can reject the call.
</Warning>

<h4 id="observability-fetchinstancelogs-return-value">
  Return value
</h4>

Returns a `FetchInstanceLogsResponse`.

<ResponseField name="logLine" type="LogLine[]">
  The matching lines. Each has a `timestamp`, `content`, the `stream` it came from, its `labels`, and a `source` such as `containers`, `kubernetes`, or `kmsg`.
</ResponseField>

<ResponseField name="paginationCursor" type="Uint8Array">
  Pass this cursor to a subsequent call to read the next page.
</ResponseField>

<ResponseField name="retentionDays" type="number">
  How long logs are retained for the workspace, in days.
</ResponseField>

***

## `compute.getInstanceMetrics()`

Return resource usage metrics for an instance. Metrics can be queried while the instance is still running.

<h3 id="compute-getinstancemetrics-example">
  Example
</h3>

```typescript {4-9} theme={null}
import { timestampFromDate } from "@bufbuild/protobuf/wkt";
import { GetInstanceMetricsRequest_MetricResource } from "@namespacelabs/sdk/proto/namespace/cloud/compute/v1beta/compute_pb";

const { timeSeries } = await client.compute.getInstanceMetrics({
  instanceId,
  startTimestamp: timestampFromDate(new Date(Date.now() - 60 * 60 * 1000)),
  metricResource: [
    GetInstanceMetricsRequest_MetricResource.CPU,
    GetInstanceMetricsRequest_MetricResource.MEMORY,
  ],
});

console.log(timeSeries.length);
```

<h3 id="compute-getinstancemetrics-api-reference">
  API reference
</h3>

```typescript theme={null}
getInstanceMetrics(
  request: MessageInitShape<typeof GetInstanceMetricsRequestSchema>,
  options?: CallOptions,
): Promise<GetInstanceMetricsResponse>
```

<h4 id="compute-getinstancemetrics-arguments-and-options">
  Arguments and options
</h4>

<ResponseField name="instanceId" type="string" required>
  The instance to read metrics for.
</ResponseField>

<ResponseField name="startTimestamp" type="Timestamp">
  Only return metrics on or after this timestamp.
</ResponseField>

<ResponseField name="endTimestamp" type="Timestamp">
  Only return metrics up to this timestamp.
</ResponseField>

<ResponseField name="metricResource" type="MetricResource[]">
  Which metrics to return: `CPU`, `CPU_BREAKDOWN`, `IO_WAIT`, `MEMORY`, or `STORAGE`.
</ResponseField>

<ResponseField name="options" type="CallOptions">
  Cancellation, timeout, and header options. See [Shared call options](/docs/reference/typescript-sdk/compute/manage#shared-call-options).
</ResponseField>

<h4 id="compute-getinstancemetrics-return-value">
  Return value
</h4>

Returns a `GetInstanceMetricsResponse` with `timeSeries`, one or more series carrying the requested metrics. Each series holds a shared `timestamps` array with named `doubles`, `integers`, and `strings` series aligned to it.

If the instance does not exist, the call fails with `NotFound`.

## `observability.fetchInstanceEgress()`

Fetch the egress records collected for an instance. Each record captures a resolved domain and the action that the [egress policy](/docs/security/egress-policy) applied.

<h3 id="observability-fetchinstanceegress-example">
  Example
</h3>

```typescript {1-4} theme={null}
const page = await client.observability.fetchInstanceEgress({
  instanceId,
  limit: 1000,
});

for (const record of page.records) {
  console.log(record.timestamp, record.domain, record.action, record.ruleMatch);
}
```

<h3 id="observability-fetchinstanceegress-api-reference">
  API reference
</h3>

```typescript theme={null}
fetchInstanceEgress(
  request: MessageInitShape<typeof FetchInstanceEgressRequestSchema>,
  options?: CallOptions,
): Promise<FetchInstanceEgressResponse>
```

<h4 id="observability-fetchinstanceegress-arguments-and-options">
  Arguments and options
</h4>

<ResponseField name="instanceId" type="string" required>
  The instance to read egress records for.
</ResponseField>

<ResponseField name="timestampRange" type="TimestampRange">
  Restricts the records to a time range, as `after` and `before` timestamps.
</ResponseField>

<ResponseField name="limit" type="number" default="20000">
  The maximum number of records to return.
</ResponseField>

<ResponseField name="paginationCursor" type="Uint8Array">
  The `paginationCursor` from a previous response, to read the next page.
</ResponseField>

<ResponseField name="options" type="CallOptions">
  Cancellation, timeout, and header options. See [Shared call options](/docs/reference/typescript-sdk/compute/manage#shared-call-options).
</ResponseField>

<h4 id="observability-fetchinstanceegress-return-value">
  Return value
</h4>

Returns a `FetchInstanceEgressResponse` with `records` and a `paginationCursor`. Each `EgressRecord` carries:

<ResponseField name="timestamp" type="Timestamp">
  When the request was observed.
</ResponseField>

<ResponseField name="domain" type="string">
  The domain that was resolved.
</ResponseField>

<ResponseField name="action" type="EgressAction">
  The action applied: `ALLOW`, `DENY`, `ADVISORY_DENY`, or `ACTION_UNKNOWN`.
</ResponseField>

<ResponseField name="ruleMatch" type="string">
  The rule matcher that led to the action, when one applied.
</ResponseField>

<ResponseField name="answerIps" type="string[]">
  The IP addresses the domain resolved to.
</ResponseField>

***

## `compute.listInstanceNotifications()`

List instance notification events. Each instance appears at most once, carrying its most recent state change.

<h3 id="compute-listinstancenotifications-example">
  Example
</h3>

```typescript {1-3} theme={null}
const { events } = await client.compute.listInstanceNotifications({
  onlyPendingEvents: true,
});

for (const event of events) {
  console.log(event.emittedAt, event.instanceId, event.status);
}
```

<h3 id="compute-listinstancenotifications-api-reference">
  API reference
</h3>

```typescript theme={null}
listInstanceNotifications(
  request: MessageInitShape<typeof ListInstanceNotificationsRequestSchema>,
  options?: CallOptions,
): Promise<ListInstanceNotificationsResponse>
```

<h4 id="compute-listinstancenotifications-arguments-and-options">
  Arguments and options
</h4>

<ResponseField name="instanceIds" type="StringMatcher">
  Only return events for matching instances. When omitted, events for all instances are returned.
</ResponseField>

<ResponseField name="limit" type="bigint">
  The maximum number of events to return. The response is always capped to an internal maximum as well.
</ResponseField>

<ResponseField name="onlyPendingEvents" type="boolean">
  When `true`, only returns pending events that have not yet been delivered.
</ResponseField>

<ResponseField name="options" type="CallOptions">
  Cancellation, timeout, and header options. See [Shared call options](/docs/reference/typescript-sdk/compute/manage#shared-call-options).
</ResponseField>

<h4 id="compute-listinstancenotifications-return-value">
  Return value
</h4>

Returns a `ListInstanceNotificationsResponse` with `events`, ordered by emitted time with the most recent first. Each `InstanceEventMetadata` carries `emittedAt`, `instanceId`, `tenantId`, the instance `status`, and its `labels`.

## Related documentation

<Columns cols={3}>
  <Card title="Compute observability" icon="activity" href="/docs/architecture/compute/observability">
    Logs, metrics, and instance debugging on the platform.
  </Card>

  <Card title="Egress policy" icon="shield" href="/docs/security/egress-policy">
    Control and audit outbound traffic from instances.
  </Card>

  <Card title="Run commands" icon="terminal" href="/docs/reference/typescript-sdk/compute/command">
    Execute commands in a container on an instance.
  </Card>
</Columns>
