Go monk

Push GitHub Actions logs to CloudWatch

GitHub keeps workflow run logs for 90 days by default, then deletes them. If you need to keep them longer - for audits, incident postmortems, or just peace of mind - you have to ship them somewhere else yourself. So I wrote workflow-logs-to-aws, a small Go CLI and GitHub Action that pushes job logs from a workflow run into AWS CloudWatch Logs.

It’s a plain Go program under the hood: no framework, just the go-github and AWS SDK v2 clients wired together.

runAttempts, err := ghc.WorkflowRunAttempts(ctx, runID)
...
for _, runAttempt := range runAttempts {
    jobs, err := ghc.WorkflowJobs(ctx, runID, runAttempt)
    ...
    for _, job := range jobs {
        logs, err := ghc.DownloadJobLogs(ctx, *job.ID)
        ...
        err = cw.UploadJobLog(ctx, logs, *job.WorkflowName, runID, runAttempt, *job.ID, *job.Name, logGroup, retentionDays, replace)
    }
}

For each completed job it downloads the log, ensures a CloudWatch log group and stream exist, and writes the log lines as CloudWatch events, parsed from GitHub’s own timestamped log format. Run it as a standalone binary:

$ go install github.com/go-monk/workflow-logs-to-aws@latest
$ workflow-logs-to-aws -repository owner/repo 123456789

(123456789 is the workflow run ID, found at the end of a run’s URL: github.com/owner/repo/actions/runs/123456789.)

or wire it into CI as an action, triggered after another workflow finishes:

on:
  workflow_run:
    workflows: [Some workflow]
    types: [completed]

permissions:
  id-token: write
  actions: read

jobs:
  push-logs:
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubWorkflowLogsWriter
          aws-region: eu-central-1
      - uses: go-monk/workflow-logs-to-aws@v0
        with:
          retention-days: 30

Credentials come from whatever AWS action ran before it (no secrets baked in) - the recommended one is aws-actions/configure-aws-credentials, as used in the example above - and the action itself runs from a prebuilt image on GHCR, so there’s no build step on every CI run.

I also added an -emf flag that, alongside the raw logs, emits CloudWatch EMF events per job - job count, failures, and duration - so you get workflow metrics and dashboards for free, without a separate metrics pipeline.

It’s a small tool doing one thing: taking logs that would otherwise vanish after 90 days and putting them somewhere durable and queryable, using infrastructure (CloudWatch, OIDC roles) you probably already have.