Skip to content

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

@deiga

Description

@deiga

Summary

This issue tracks the migration from Go's standard log package to HashiCorp's structured logging package tflog across the provider codebase.

Resolves #2926, #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

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 ✅

  • resource_github_membership.go
  • 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 ([MAINT] Migrate all resources and data sources to Context-aware CRUD 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

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 ([MAINT] Migrate all resources and data sources to Context-aware CRUD functions #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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Good first issueGood for newcomersStatus: Up for grabsIssues that are ready to be worked on by anyoneType: MaintenanceAny dependency, housekeeping, and clean up Issue or PR

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions