# Logs and metrics

> Read and follow a deployment's logs, forward them to your own service with a drain, and watch its requests, errors, latency and alerts.

Source: https://www.coritan.com/docs/managed-containers/logs-and-metrics/

In the dashboard:

- /dashboard/deployments/…/logs: https://www.coritan.com/dashboard/deployments
- /dashboard/deployments/…/metrics: https://www.coritan.com/dashboard/deployments

What your app prints goes to its logs, which you can search, follow live and forward to a service of your own. The deployment's metrics count its requests, errors, latency and bandwidth for every hour, and alerts tell you by email when a release fails or errors climb. The **Logs** and **Metrics** tabs hold them for a web service or a hybrid site, and a static site has the **Metrics** tab. A game server's or a database's output is in its console instead ([Use the console](/docs/managed-containers/console/)).

## Before you begin

- Logs are kept for as long as your plan says: a day on Free, 7 days on Pro, 30 days on Business and 90 days on Enterprise ([Pricing](https://www.coritan.com/pricing)).
- Log drains are part of the Pro, Business and Enterprise plans.

## Read the logs {#read-logs}

Open the deployment and select the **Logs** tab. The newest lines are at the bottom, each with its time, the instance and process that printed it, and the location. You can:

- search for lines that hold a word, ignoring case;
- narrow the lines to one instance or one process, such as `web` or `worker`;
- go back to a time, such as two hours ago, and page through older lines;
- follow new lines live as the instances print them.

Lines are collected from the instances every minute, so a line's time is when it was collected, within a minute of when it was printed. When an instance printed faster than we could collect, a line is marked as coming after a gap, because lines may be missing just before it. An instance that stops is read once more, so its last words are kept.

A live tail shows each instance's recent lines, then follows new ones for up to 15 minutes, including instances that a new release starts meanwhile. You can have up to 5 live tails open at once. From a terminal, `coritan logs -f` does the same ([The Coritan CLI](/docs/managed-containers/cli/)).

## Forward logs with a drain {#drains}

A drain sends the deployment's lines to a service of your own as they are collected, such as a log platform or your own server:

HTTP
: Each batch is a `POST` of newline-delimited JSON to an `https://` address. Each request is signed, so your service can check it came from us ([Check a drain's signature](#check-a-drains-signature)).

Syslog
: RFC 5424 messages over TLS to an address such as `syslog+tls://logs.example.com:6514`.

A drain can take only some processes, or only production or previews. It starts with the newest line collected when you create it, not the history. Delivery is at least once, so a batch that failed can arrive twice: remove duplicates by each line's `id`. Lines more than an hour old when a drain reaches them are skipped. A drain that fails every minute for an hour is turned off, and says why. Send a test line at any time to check it.

Your plan sets how many drains your account can have, counted across all its deployments, and a deployment can have up to 10.

## Check a drain's signature {#check-a-drains-signature}

An HTTP drain has a secret, which we generate unless you give one of 16 to 200 characters, and which is shown only once. Each request carries `X-Coritan-Signature: t=<unix time>,v1=<signature>`, where the signature is the HMAC SHA-256 of the time, a full stop and the raw body, keyed with the secret:

```python
import hashlib, hmac, time

def verify(secret: str, header: str, body: bytes, tolerance: int = 300) -> bool:
    parts = dict(item.split("=", 1) for item in header.split(","))
    t = int(parts["t"])
    expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
    return abs(time.time() - t) <= tolerance and hmac.compare_digest(expected, parts["v1"])
```

Each line is an object with `id`, `ts`, `deployment`, `deployment_name`, `instance`, `process`, `location`, `release`, `environment`, `message` and `gap`. A test line also has `"test": true`.

## Watch the metrics {#metrics}

The **Metrics** tab shows the last 24 hours, 7 days or 30 days, hour by hour: requests, how many were blocked by the firewall, answers by status (`2xx`, `3xx`, `4xx`, `5xx`), bandwidth in and out, and the average and slowest latency. It also shows each running instance's processor and memory use now and over the last hour, day or week, kept as long as your plan keeps logs. You can look at one address at a time, such as a preview's.

Recent requests are sampled at every location and kept for 3 days: for each address, every request up to 5 a second, then one in a hundred, and every `5xx` answer. Filter them by address, answer or path to find the path behind a burst of errors.

Page views and visitors are counted for the pages that load the Coritan tracking script, over 7, 30 or 90 days. Requests count everything; page views count only people who loaded a page with the script.

## Get alerts {#alerts}

We email the deployment's owner, or an organization's owner, when:

- a production release fails;
- errors climb: in one hour, at least 100 requests of which 10% or more were `5xx` answers;
- visitors kept waking a sleeping deployment: 25 requests or more in an hour while it was asleep ([Sleep when idle](/docs/managed-containers/locations-and-scaling/#sleep-when-idle)).

Alert emails follow your **Usage alerts** preference ([Notifications](/docs/account/notifications/)). An organization's webhooks get the same alerts as events.

## Result

The **Logs** tab shows what the instances printed, and a drain's last delivery says when lines last reached your service.

## Troubleshooting

The logs are empty
: Lines are collected every minute, so a new instance's first lines take up to a minute to appear. A static site has no instances and no logs.

A drain turned itself off
: It failed for an hour. Its last error says why, such as a refused connection or a `401`. Fix the address or the secret, then turn it on again, which starts it from the newest line.

`too_many_streams`
: You have 5 live tails open. Close one, or wait for one to end.

The metrics for the current hour are empty
: Each hour fills in after it ends.

## Related

- [Deploy, promote and roll back releases](/docs/managed-containers/releases/) explains the build log of each release.
- [Choose locations and scaling](/docs/managed-containers/locations-and-scaling/) explains instances and sleeping.

## With the API

The routes are under `/api/v1/client/deployments/{uuid}` for your own deployment and `/api/v1/orgs/{org_slug}/deployments/{uuid}` for an organization's. Any member of an organization reads them; owners and admins change drains.

`GET /logs` answers the newest `limit` lines (100 unless you ask for up to 1,000), oldest first, with `since`, `until`, `instance`, `process` and `q` to narrow them. `since` and `until` take a time or how long ago, such as `2h`. Pass the answer's `next` as `before` for older lines, and add `format=ndjson` for one line object a line:

```bash
curl "https://api.coritan.com/api/v1/client/deployments/$DEPLOYMENT/logs?since=2h&q=timeout" \
  -H "Authorization: Bearer $CORITAN_TOKEN"
```

`GET /logs/stream` follows new lines as Server-Sent Events, with `tail` (up to 200 lines to start with) and `seconds` (up to 900). `GET /logs/instances` lists the instances with lines kept, and their `id` for `instance`.

`POST /drains` with `{"kind": "http", "url": "https://intake.example.com/v1"}` creates a drain and answers its generated `secret` this once. `PATCH /drains/{id}` changes it, `POST /drains/{id}/test` sends a test line, and `DELETE /drains/{id}` removes it.

`GET /metrics?window=7d`, `GET /requests?status=5xx`, `GET /analytics?window=30d` and `GET /alerts` answer what the **Metrics** tab shows.

## API

- `GET /api/v1/client/deployments/{deployment_uuid}/logs`: Get logs (https://www.coritan.com/docs/api/reference/client/deployments/deployments-logs/#op-get-api-v1-client-deployments-deployment-uuid-logs)
- `GET /api/v1/client/deployments/{deployment_uuid}/logs/instances`: Get log instances (https://www.coritan.com/docs/api/reference/client/deployments/deployments-logs/#op-get-api-v1-client-deployments-deployment-uuid-logs-instances)
- `GET /api/v1/client/deployments/{deployment_uuid}/logs/stream`: Stream logs (https://www.coritan.com/docs/api/reference/client/deployments/deployments-logs/#op-get-api-v1-client-deployments-deployment-uuid-logs-stream)
- `GET /api/v1/client/deployments/{deployment_uuid}/drains`: List drains (https://www.coritan.com/docs/api/reference/client/deployments/deployments-drains/#op-get-api-v1-client-deployments-deployment-uuid-drains)
- `POST /api/v1/client/deployments/{deployment_uuid}/drains`: A drain that forwards the lines collected from now on (https://www.coritan.com/docs/api/reference/client/deployments/deployments-drains/#op-post-api-v1-client-deployments-deployment-uuid-drains)
- `PATCH /api/v1/client/deployments/{deployment_uuid}/drains/{drain_id}`: Update drain (https://www.coritan.com/docs/api/reference/client/deployments/deployments-drains/#op-patch-api-v1-client-deployments-deployment-uuid-drains-drain-id)
- `DELETE /api/v1/client/deployments/{deployment_uuid}/drains/{drain_id}`: Remove the drain; nothing more is sent to it (https://www.coritan.com/docs/api/reference/client/deployments/deployments-drains/#op-delete-api-v1-client-deployments-deployment-uuid-drains-drain-id)
- `POST /api/v1/client/deployments/{deployment_uuid}/drains/{drain_id}/test`: Test drain (https://www.coritan.com/docs/api/reference/client/deployments/deployments-drains/#op-post-api-v1-client-deployments-deployment-uuid-drains-drain-id-test)
- `GET /api/v1/client/deployments/{deployment_uuid}/requests`: Get requests (https://www.coritan.com/docs/api/reference/client/deployments/deployments/#op-get-api-v1-client-deployments-deployment-uuid-requests)
- `GET /api/v1/client/deployments/{deployment_uuid}/metrics`: Get metrics (https://www.coritan.com/docs/api/reference/client/deployments/deployments/#op-get-api-v1-client-deployments-deployment-uuid-metrics)
- `GET /api/v1/client/deployments/{deployment_uuid}/analytics`: Get analytics (https://www.coritan.com/docs/api/reference/client/deployments/deployments/#op-get-api-v1-client-deployments-deployment-uuid-analytics)
- `GET /api/v1/client/deployments/{deployment_uuid}/alerts`: Get alerts (https://www.coritan.com/docs/api/reference/client/deployments/deployments/#op-get-api-v1-client-deployments-deployment-uuid-alerts)
- `GET /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/logs`: Get logs (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments-logs/#op-get-api-v1-orgs-org-slug-deployments-deployment-uuid-logs)
- `GET /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/logs/instances`: Get log instances (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments-logs/#op-get-api-v1-orgs-org-slug-deployments-deployment-uuid-logs-instances)
- `GET /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/logs/stream`: Stream logs (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments-logs/#op-get-api-v1-orgs-org-slug-deployments-deployment-uuid-logs-stream)
- `GET /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/drains`: List drains (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments-drains/#op-get-api-v1-orgs-org-slug-deployments-deployment-uuid-drains)
- `POST /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/drains`: A drain that forwards the lines collected from now on (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments-drains/#op-post-api-v1-orgs-org-slug-deployments-deployment-uuid-drains)
- `PATCH /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/drains/{drain_id}`: Update drain (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments-drains/#op-patch-api-v1-orgs-org-slug-deployments-deployment-uuid-drains-drain-id)
- `DELETE /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/drains/{drain_id}`: Remove the drain; nothing more is sent to it (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments-drains/#op-delete-api-v1-orgs-org-slug-deployments-deployment-uuid-drains-drain-id)
- `POST /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/drains/{drain_id}/test`: Test drain (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments-drains/#op-post-api-v1-orgs-org-slug-deployments-deployment-uuid-drains-drain-id-test)
- `GET /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/requests`: Get requests (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments/#op-get-api-v1-orgs-org-slug-deployments-deployment-uuid-requests)
- `GET /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/metrics`: Get metrics (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments/#op-get-api-v1-orgs-org-slug-deployments-deployment-uuid-metrics)
- `GET /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/analytics`: Get analytics (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments/#op-get-api-v1-orgs-org-slug-deployments-deployment-uuid-analytics)
- `GET /api/v1/orgs/{org_slug}/deployments/{deployment_uuid}/alerts`: Get alerts (https://www.coritan.com/docs/api/reference/organizations/organization-deployments/deployments/#op-get-api-v1-orgs-org-slug-deployments-deployment-uuid-alerts)
