Summary
This issue tracks the migration from Go's standard log package to HashiCorp's structured logging package tflog across the provider codebase.
Resolves #2629
Why?
The tflog package provides several benefits over standard log:
Structured logging - Separate log messages from filterable field data for programmatic parsing
Better integration - Works properly with Terraform's logging system (TF_LOG, TF_LOG_PROVIDER)
Filtering capabilities - Users can filter logs by fields, making debugging easier
Consistent patterns - Aligns with HashiCorp's official Terraform provider development practices
Sensitive data masking - Built-in support for masking sensitive values in logs
Related Issues
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:
// 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:
ctx = tflog .SetField (ctx , "repository" , repoName )
tflog .Debug (ctx , "Reading secret" ) // includes repository field automatically
Before/After Examples
Before (current pattern)
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)
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 ✅
Resources (50 files)
Data Sources (9 files)
Utilities & Core (10 files)
Migrations (4 files)
Implementation Notes
Context requirement : All tflog functions require a context.Context from the SDK
Pair with Context migration : If a file uses legacy CRUD functions, migrate to Context-aware functions ([MAINT] Migrate all resources and data sources to Context-aware CRUD functions #2996 ) in the same PR
Import change : Replace "log" with "github.com/hashicorp/terraform-plugin-log/tflog"
Field naming : Use consistent field names across the codebase (e.g., repository, owner, team_slug)
Message format : Don't use fmt.Sprintf() for complex messages, instead use the map for all data inputs
References
Contributing
This is a good first issue for contributors! Each file can be refactored independently. When contributing:
Pick a file from the list above
Check if the file uses legacy CRUD functions - if so, migrate to Context-aware functions first ([MAINT] Migrate all resources and data sources to Context-aware CRUD functions #2996 )
Replace log imports with tflog
Convert log.Printf("[LEVEL] ...") calls to tflog.Level(ctx, ..., map[string]any{...})
Run make build and make lint to verify
Submit a PR referencing this issue
/cc @maintainers
Summary
This issue tracks the migration from Go's standard
logpackage to HashiCorp's structured logging packagetflogacross the provider codebase.Resolves #2629
Why?
The
tflogpackage provides several benefits over standardlog:TF_LOG,TF_LOG_PROVIDER)Related Issues
transport.goBest Practices (from HashiCorp Documentation)
Log Levels (least to most verbose)
Structured Fields
Use structured log fields instead of embedding values in message strings:
Persistent Context
Use
tflog.SetField()to attach fields to all subsequent logs in a function:Before/After Examples
Before (current pattern)
After (target pattern)
Files to Refactor
Already Completed ✅
resource_github_membership.goresource_github_organization_ruleset.goResources (50 files)
resource_github_actions_environment_secret.goresource_github_actions_environment_variable.goresource_github_actions_hosted_runner.goresource_github_actions_organization_permissions.goresource_github_actions_organization_secret_repository.goresource_github_actions_organization_secret.goresource_github_actions_organization_variable.goresource_github_actions_repository_permissions.goresource_github_actions_runner_group.goresource_github_actions_secret.goresource_github_actions_variable.goresource_github_app_installation_repositories.goresource_github_app_installation_repository.goresource_github_branch_default.goresource_github_branch_protection_v3.goresource_github_branch_protection.goresource_github_branch.goresource_github_codespaces_organization_secret.goresource_github_codespaces_secret.goresource_github_codespaces_user_secret.goresource_github_dependabot_organization_secret.goresource_github_dependabot_secret.goresource_github_enterprise_actions_runner_group.goresource_github_enterprise_actions_workflow_permissions.goresource_github_enterprise_organization.goresource_github_enterprise_security_analysis_settings.goresource_github_issue_label.goresource_github_issue_labels.goresource_github_issue.goresource_github_organization_custom_role.goresource_github_organization_repository_role.goresource_github_organization_role_team_assignment.goresource_github_organization_role_team.goresource_github_organization_role_user.goresource_github_organization_role.goresource_github_organization_security_manager.goresource_github_organization_settings.goresource_github_organization_webhook.goresource_github_release.goresource_github_repository_autolink_reference.goresource_github_repository_collaborator.goresource_github_repository_collaborators.goresource_github_repository_deploy_key.goresource_github_repository_deployment_branch_policy.goresource_github_repository_environment_deployment_policy.goresource_github_repository_environment.goresource_github_repository_file.goresource_github_repository_milestone.goresource_github_repository_pull_request.goresource_github_repository_ruleset.goresource_github_repository_topics.goresource_github_repository_webhook.goresource_github_repository.goresource_github_team_members.goresource_github_team_membership.goresource_github_team_repository.goresource_github_team_sync_group_mapping.goresource_github_team.goresource_github_user_gpg_key.goresource_github_user_ssh_key.goresource_organization_block.goData Sources (9 files)
data_source_github_actions_organization_registration_token.godata_source_github_actions_registration_token.godata_source_github_branch.godata_source_github_codespaces_public_key.godata_source_github_dependabot_public_key.godata_source_github_organization_custom_role.godata_source_github_ref.godata_source_github_repository_file.godata_source_github_repository.goUtilities & Core (10 files)
provider.gotransport.gorepository_utils.goutil.goutil_rules.goutil_v4_branch_protection.goresource_github_branch_protection_v3_utils.goMigrations (4 files)
migrate_github_actions_organization_secret.gomigrate_github_actions_secret.gomigrate_github_repository_webhook.gomigrate_github_repository.goImplementation Notes
tflogfunctions require acontext.Contextfrom the SDK"log"with"github.com/hashicorp/terraform-plugin-log/tflog"repository,owner,team_slug)fmt.Sprintf()for complex messages, instead use the map for all data inputsReferences
Contributing
This is a good first issue for contributors! Each file can be refactored independently. When contributing:
logimports withtfloglog.Printf("[LEVEL] ...")calls totflog.Level(ctx, ..., map[string]any{...})make buildandmake lintto verify/cc @maintainers