integrations / integrations/terraform-provider-github

[MAINT] Tracking issue for refactoring logging to use `tflog` instead of `log`

Open
#3,070 1 comment 0 reactions 0 assignees View on GitHub
Good first issue Status: Up for grabs Type: Maintenance
Dominant language
Go
Stars
1.2k
Forks
1k
Avg merge
1d 14h
Merged PRs (30d)
8

Description

## Summary

This issue tracks the migration from Go's standard `log` package to HashiCorp's structured logging package [`tflog`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-log/tflog) across the provider codebase.

Resolves #2629

## Why?

The `tflog` package provides several benefits over standard `log`:

1. **Structured logging** - Separate log messages from filterable field data for programmatic parsing
2. **Better integration** - Works properly with Terraform's logging system (`TF_LOG`, `TF_LOG_PROVIDER`)
3. **Filtering capabilities** - Users can filter logs by fields, making debugging easier
4. **Consistent patterns** - Aligns with HashiCorp's official Terraform provider development practices
5. **Sensitive data masking** - Built-in support for masking sensitive values in logs

## Related Issues

- **#2996** - [MAINT] Migrate all resources and data sources to Context-aware CRUD functions - **Should be paired with this work** (see note below)
- **#2679** - [BUG]: Insufficient logging - Requests more logging output; better structured logging framework would help address this
- **#1226** - Provider hangs, without giving any logging or feedback - Rate limit logging would be improved with tflog in `transport.go`
- **#2925** - [MAINT]: Update transport implementation - Transport changes may want to coordinate with logging refactoring

> **Important**: The `tflog` package requires a `context.Context` parameter. Files that still use legacy CRUD functions (e.g., `Create`, `Read` instead of `CreateContext`, `ReadContext`) should be migrated to Context-aware functions as part of the same PR. See #2996 for details on the Context migration.

## Best Practices (from HashiCorp Documentation)

### Log Levels (least to most verbose)
- **Error** - Unexpected conditions before halting execution
- **Warn** - Unexpected conditions that don't stop execution (deprecations, external changes)
- **Info** - Documents logic conditions or events (state changes, decisions)
- **Debug** - Operational milestones and behaviors
- **Trace** - Intra-function steps and raw data details

### Structured Fields
Use structured log fields instead of embedding values in message strings:

```go
// Good: Structured fields for filtering
tflog.Debug(ctx, "Deleting secret", map[string]any{
"repository": repoName,
"secret_name": secretName,
})

// Bad: Embedded values
log.Printf("[DEBUG] Deleting secret: %s/%s", repoName, secretName)
```

### Persistent Context
Use `tflog.SetField()` to attach fields to all subsequent logs in a function:

```go
ctx = tflog.SetField(ctx, "repository", repoName)
tflog.Debug(ctx, "Reading secret") // includes repository field automatically
```

## Before/After Examples

### Before (current pattern)

```go
import (
"log"
)

func resourceGithubDependabotSecretRead(d *schema.ResourceData, meta any) error {
// ...
if ghErr.Response.StatusCode == http.StatusNotFound {
log.Printf("[WARN] Removing actions secret %s from state because it no longer exists in GitHub",
d.Id())
d.SetId("")
return nil
}
// ...
log.Printf("[DEBUG] Deleting secret: %s", d.Id())
}
```

### After (target pattern)

```go
import (
"github.com/hashicorp/terraform-plugin-log/tflog"
)

func resourceGithubDependabotSecretRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
// ...
if ghErr.Response.StatusCode == http.StatusNotFound {
tflog.Warn(ctx, "Removing secret from state because it no longer exists in GitHub", map[string]any{
"secret_id": d.Id(),
"repository": repoName,
})
d.SetId("")
return nil
}
// ...
tflog.Debug(ctx, "Deleting secret", map[string]any{
"secret_id": d.Id(),
"repository": repoName,
"secret_name": secretName,
})
}
```

## Files to Refactor

### Already Completed ✅
- [x] `resource_github_membership.go`
- [x] `resource_github_organization_ruleset.go`

### Resources (50 files)
- [ ] `resource_github_actions_environment_secret.go`
- [ ] `resource_github_actions_environment_variable.go`
- [ ] `resource_github_actions_hosted_runner.go`
- [ ] `resource_github_actions_organization_permissions.go`
- [ ] `resource_github_actions_organization_secret_repository.go`
- [ ] `resource_github_actions_organization_secret.go`
- [ ] `resource_github_actions_organization_variable.go`
- [ ] `resource_github_actions_repository_permissions.go`
- [ ] `resource_github_actions_runner_group.go`
- [ ] `resource_github_actions_secret.go`
- [ ] `resource_github_actions_variable.go`
- [ ] `resource_github_app_installation_repositories.go`
- [ ] `resource_github_app_installation_repository.go`
- [ ] `resource_github_branch_default.go`
- [ ] `resource_github_branch_protection_v3.go`
- [ ] `resource_github_branch_protection.go`
- [ ] `resource_github_branch.go`
- [ ] `resource_github_codespaces_organization_secret.go`
- [ ] `resource_github_codespaces_secret.go`
- [ ] `resource_github_codespaces_user_secret.go`
- [ ] `resource_github_dependabot_organization_secret.go`
- [ ] `resource_github_dependabot_secret.go`
- [ ] `resource_github_enterprise_actions_runner_group.go`
- [ ] `resource_github_enterprise_actions_workflow_permissions.go`
- [ ] `resource_github_enterprise_organization.go`
- [ ] `resource_github_enterprise_security_analysis_settings.go`
- [ ] `resource_github_issue_label.go`
- [ ] `resource_github_issue_labels.go`
- [ ] `resource_github_issue.go`
- [ ] `resource_github_organization_custom_role.go`
- [ ] `resource_github_organization_repository_role.go`
- [ ] `resource_github_organization_role_team_assignment.go`
- [ ] `resource_github_organization_role_team.go`
- [ ] `resource_github_organization_role_user.go`
- [ ] `resource_github_organization_role.go`
- [ ] `resource_github_organization_security_manager.go`
- [ ] `resource_github_organization_settings.go`
- [ ] `resource_github_organization_webhook.go`
- [ ] `resource_github_release.go`
- [ ] `resource_github_repository_autolink_reference.go`
- [ ] `resource_github_repository_collaborator.go`
- [ ] `resource_github_repository_collaborators.go`
- [ ] `resource_github_repository_deploy_key.go`
- [ ] `resource_github_repository_deployment_branch_policy.go`
- [ ] `resource_github_repository_environment_deployment_policy.go`
- [ ] `resource_github_repository_environment.go`
- [ ] `resource_github_repository_file.go`
- [ ] `resource_github_repository_milestone.go`
- [ ] `resource_github_repository_pull_request.go`
- [ ] `resource_github_repository_ruleset.go`
- [ ] `resource_github_repository_topics.go`
- [ ] `resource_github_repository_webhook.go`
- [ ] `resource_github_repository.go`
- [ ] `resource_github_team_members.go`
- [ ] `resource_github_team_membership.go`
- [ ] `resource_github_team_repository.go`
- [ ] `resource_github_team_sync_group_mapping.go`
- [ ] `resource_github_team.go`
- [ ] `resource_github_user_gpg_key.go`
- [ ] `resource_github_user_ssh_key.go`
- [ ] `resource_organization_block.go`

### Data Sources (9 files)
- [ ] `data_source_github_actions_organization_registration_token.go`
- [ ] `data_source_github_actions_registration_token.go`
- [ ] `data_source_github_branch.go`
- [ ] `data_source_github_codespaces_public_key.go`
- [ ] `data_source_github_dependabot_public_key.go`
- [ ] `data_source_github_organization_custom_role.go`
- [ ] `data_source_github_ref.go`
- [ ] `data_source_github_repository_file.go`
- [ ] `data_source_github_repository.go`

### Utilities & Core (10 files)
- [ ] `provider.go`
- [ ] `transport.go`
- [ ] `repository_utils.go`
- [ ] `util.go`
- [ ] `util_rules.go`
- [ ] `util_v4_branch_protection.go`
- [ ] `resource_github_branch_protection_v3_utils.go`

### Migrations (4 files)
- [ ] `migrate_github_actions_organization_secret.go`
- [ ] `migrate_github_actions_secret.go`
- [ ] `migrate_github_repository_webhook.go`
- [ ] `migrate_github_repository.go`

## Implementation Notes

1. **Context requirement**: All `tflog` functions require a `context.Context` from the SDK
2. **Pair with Context migration**: If a file uses legacy CRUD functions, migrate to Context-aware functions (#2996) in the same PR
3. **Import change**: Replace `"log"` with `"github.com/hashicorp/terraform-plugin-log/tflog"`
4. **Field naming**: Use consistent field names across the codebase (e.g., `repository`, `owner`, `team_slug`)
5. **Message format**: Don't use `fmt.Sprintf()` for complex messages, instead use the map for all data inputs

## References

- [HashiCorp: Writing Log Output](https://developer.hashicorp.com/terraform/plugin/log/writing)
- [HashiCorp: Managing Log Output](https://developer.hashicorp.com/terraform/plugin/log/managing)
- [HashiCorp: Filtering Log Output](https://developer.hashicorp.com/terraform/plugin/log/filtering)
- [tflog Package Documentation](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-log/tflog)
- [AWS Provider Issue #24165](https://github.com/hashicorp/terraform-provider-aws/issues/24165) - Similar refactoring discussion

## Contributing

This is a good first issue for contributors! Each file can be refactored independently. When contributing:

1. Pick a file from the list above
2. Check if the file uses legacy CRUD functions - if so, migrate to Context-aware functions first (#2996)
3. Replace `log` imports with `tflog`
4. Convert `log.Printf("[LEVEL] ...")` calls to `tflog.Level(ctx, ..., map[string]any{...})`
5. Run `make build` and `make lint` to verify
6. Submit a PR referencing this issue

---

/cc @maintainers

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.