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

# GitHub Actions

> Send every GitHub Actions job log, with run, job and step summaries, to Sazabi from one workflow file.

[All GitHub integration capabilities](https://docs.sazabi.com/catalogs/integrations/github.md)

## About

Add one workflow to a repository and every GitHub Actions run in it sends its job logs to Sazabi, with a summary of each run, job and step: the result, how long it took and how long it queued. Your existing workflows don't change, and workflows you add later are covered too.

The workflow, `Sazabi logs`, is triggered by [`workflow_run`](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#workflow_run) whenever another workflow run completes. It runs the public [`sazabi/upload-actions-logs`](https://github.com/sazabi/upload-actions-logs) action, which reads that run's jobs and job logs through the GitHub API with the workflow's own token and sends them to your intake URL as OpenTelemetry log records. The intake URL, saved as the Actions secret `SAZABI_LOGS_URL`, is the only setup value: no GitHub App and no personal access token.

### What arrives

| Record | Identifies it | Main attributes |
| - | - | - |
| Run summary | `event.name` `github.actions.run` | `cicd.pipeline.result`, `github.conclusion`, `github.duration_ms`, `github.queue_duration_ms`, `github.job_count` |
| Job summary | `event.name` `github.actions.job` | `cicd.pipeline.task.name`, `cicd.pipeline.task.run.result`, `cicd.worker.name`, `github.runner.labels`, `github.duration_ms`, `github.queue_duration_ms` |
| Step summary | `event.name` `github.actions.step` | `github.step.name`, `github.step.number`, `github.conclusion`, `github.duration_ms` |
| Log line | `github.log.line` | The job's attributes, plus `github.step.name` and `github.step.number` when the line is matched to a step |

Every record carries the repository, branch, commit and pull request (`vcs.repository.name`, `vcs.ref.head.name`, `vcs.ref.head.revision`, `vcs.change.id`), the workflow and run (`cicd.pipeline.name`, `cicd.pipeline.run.id`, `cicd.pipeline.run.url.full`, `github.run.attempt`) and a `service.name` of the repository name. The names follow the OpenTelemetry [CI/CD](https://opentelemetry.io/docs/specs/semconv/registry/attributes/cicd/) and [VCS](https://opentelemetry.io/docs/specs/semconv/registry/attributes/vcs/) conventions. Log lines keep GitHub's timestamps; `##[error]` lines are ERROR and `##[warning]` lines are WARN.

> [!NOTE]
>
> Log lines are sent as your jobs printed them, after GitHub's masking of registered secrets. To keep other sensitive values out, turn on the project's **Redact sensitive data** setting, or send summaries only with `logs: false`.

### Action inputs

| Input | Default | Meaning |
| - | - | - |
| `url` | required | Your intake URL with `/v1/logs`, from a secret |
| `logs` | `true` | `false` sends the run, job and step summaries only |
| `max-bytes-per-job` | `10485760` | The most log text sent per job (10 MB) |
| `service-name` | repository name | The `service.name` of every record |
| `fail-on-error` | `false` | Fail the job when the upload or a job log download fails |
| `github-token` | `${{ github.token }}` | Token for the jobs and logs API; needs `actions: read` |

Over `max-bytes-per-job`, a job sends the first tenth and the last nine tenths of the cap, plus one record with `github.log.truncated` and `github.log.omitted_bytes`. An upload error is a workflow warning by default, so an outage never fails your checks.

## Prerequisites

* A repository that uses GitHub Actions, on GitHub.com or GitHub Enterprise Cloud
* Permission to add a workflow to the repository's default branch, and to add an Actions secret to the repository (or, for every repository at once, to the organization)
* Runners that can reach your Sazabi intake host over HTTPS; the workflow uses `ubuntu-latest` by default
* Your Sazabi intake URL, shown above — open the log source and copy the **Intake URL** from its setup screen (the public key is embedded in the URL)

## Set up in the dashboard

**Step 1**

### Save the intake URL as a secret

Save your intake URL, with `/v1/logs` on the end, as the Actions secret `SAZABI_LOGS_URL`: in the repository's **Settings > Secrets and variables > Actions**, or from a clone of it with the GitHub CLI:

```bash theme={"system"}
gh secret set SAZABI_LOGS_URL --body "https://<your intake host>/v1/logs"
```

To cover every repository in an organization, an organization owner can set one organization secret instead: add `--org <your organization> --visibility all` to the command. The URL carries your source's key, so keep it in a secret, never in a workflow file.

**Step 2**

### Add the workflow

Save this as `.github/workflows/sazabi-logs.yml` and merge it into the default branch. GitHub only starts `workflow_run` workflows from the default branch.

```yaml title=".github/workflows/sazabi-logs.yml" theme={"system"}
name: Sazabi logs
on:
  workflow_run:
    workflows: ["**", "!Sazabi logs"]
    types: [completed]
permissions:
  actions: read
jobs:
  upload:
    runs-on: ubuntu-latest
    steps:
      - uses: sazabi/upload-actions-logs@v1
        with:
          url: ${{ secrets.SAZABI_LOGS_URL }}
```

* `"**"` matches every workflow, and `"!Sazabi logs"` keeps this one from uploading its own runs. Add more `"!Name"` entries to leave other workflows out.
* `permissions: actions: read` lets the action read job logs with the workflow's own token. The action never checks out or runs code from the run it uploads, so pull requests from forks can't reach the secret.

**Step 3**

### Offer it to every repository (optional)

To make the workflow one click for every repository in your organization, add it as a [workflow template](https://docs.github.com/en/actions/how-tos/reuse-automations/create-workflow-templates) in the organization's public `.github` repository: save the workflow above as `workflow-templates/sazabi-logs.yml`, next to `workflow-templates/sazabi-logs.properties.json`:

```json title="workflow-templates/sazabi-logs.properties.json" theme={"system"}
{
  "name": "Sazabi logs",
  "description": "Send every workflow run's job logs and run, job and step summaries to Sazabi.",
  "categories": ["Monitoring"]
}
```

Each repository then adds it from **Actions > New workflow**, and reads the organization secret.

## Set up with the CLI

You can also register the GitHub Actions log source with the [Sazabi CLI](https://docs.sazabi.com/reference/cli.md) (installed and authenticated — see [CLI reference](https://docs.sazabi.com/reference/cli/log-sources.md)):

```bash theme={"system"}
sazabi log-sources create github_actions --mode manual
```

The command prints the intake URL for the new source. Add `/v1/logs` and save it as the `SAZABI_LOGS_URL` secret. Run `sazabi log-sources get <log-source-id>` at any time to reprint the endpoint.

## Verify

Once the workflow is on the default branch, run any other workflow, or wait for the next one. When it completes, the **Sazabi logs** workflow runs and its upload step prints how many records it sent. Open the [Intake page](https://docs.sazabi.com/guides/instrument/intake.md) and confirm records from the **GitHub Actions** source appear, then search for `event.name` `github.actions.job` or your workflow's `cicd.pipeline.name`.

## Troubleshooting

**The Sazabi logs workflow never runs** — It must be on the default branch. A run that was itself started by a chain of three `workflow_run` workflows doesn't trigger it.

**The upload step warns that `url` is empty** — The `SAZABI_LOGS_URL` secret isn't set, or the organization secret isn't shared with this repository.

**The upload step can't list jobs or download logs** — The workflow needs `permissions: actions: read`, and the organization must allow the workflow token to read Actions.

**Summaries arrive but some jobs have no lines** — GitHub had no log for those jobs (for example, jobs that never started); the step prints which.

**A warning about records older than the intake accepts** — A run that took longer than 23 hours keeps its real time in `github.original_time`.

## Further reading

> [card] [Intake](https://docs.sazabi.com/guides/instrument/intake.md)
>
> See per-source ingestion volume and health.

> [card] [Log sources](https://docs.sazabi.com/catalogs/log-sources.md)
>
> Browse the full log source catalog.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.