integrations / integrations/terraform-provider-github
[MAINT] Tracking issue for refactoring logging to use `tflog` instead of `log`
- 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
Assessment
This issue has not been assessed yet.