Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 26 additions & 22 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,21 +282,28 @@ make agent-finished
```go
import "github.com/github/gh-aw-mcpg/internal/logger"

// Create a logger with namespace following pkg:filename convention
var log = logger.New("pkg:filename")
// Create a logger with namespace auto-derived from the calling file (PREFERRED)
var logComponent = logger.ForFile()

// Log debug messages
// - Writes to stderr with colors and time diffs (when DEBUG matches namespace)
// - Also writes to file logger as text-only (always, when logger is enabled)
log.Printf("Processing %d items", count)
log.Print("Simple debug message")
logComponent.Printf("Processing %d items", count)
logComponent.Print("Simple debug message")

// Check if logging is enabled before expensive operations
if log.Enabled() {
log.Printf("Expensive debug info: %+v", expensiveOperation())
if logComponent.Enabled() {
logComponent.Printf("Expensive debug info: %+v", expensiveOperation())
}
```

`logger.ForFile()` automatically derives the namespace as `"package:filename"` from the calling
file path (e.g., a call in `internal/server/unified.go` yields `"server:unified"`). This
eliminates manually maintained namespace strings and prevents namespace drift.

Use `logger.New("pkg:component")` only when a custom namespace is intentionally different from
the file name (e.g., a file that maintains backward-compatible debug namespace for users).

**For operational/file logging, use the file logger directly:**

```go
Expand All @@ -311,7 +318,7 @@ logger.LogError("category", "Operation failed: %v", err)
logger.LogDebug("category", "Debug details: %+v", details)
```

**Note:** Debug loggers created with `logger.New()` now write to both stderr (with colors/time diffs) and the file logger (text-only). This provides real-time colored output during development while ensuring all debug logs are captured to file for production troubleshooting.
**Note:** Debug loggers created with `logger.ForFile()` and `logger.New()` write to both stderr (with colors/time diffs) and the file logger (text-only). This provides real-time colored output during development while ensuring all debug logs are captured to file for production troubleshooting.

**Logging Categories:**
- `startup` - Gateway initialization and configuration
Expand All @@ -326,26 +333,23 @@ logger.LogDebug("category", "Debug details: %+v", details)
- Be consistent with existing loggers in the codebase

**Logger Variable Naming Convention:**
- **Use descriptive names** that match the component: `var log<Component> = logger.New("pkg:component")`
- Examples: `var logLauncher = logger.New("launcher:launcher")`, `var logConfig = logger.New("config:config")`
- **Use descriptive names** that match the component: `var log<Component> = logger.ForFile()`
- Examples: `var logLauncher = logger.ForFile()`, `var logHandlers = logger.ForFile()`
- **Avoid generic `log` name** when it might conflict with standard library or when the file already imports `log` package
- Capitalize the component part after 'log' (e.g., `logAuth` with capital 'A', `logLauncher` with capital 'L')
- This convention makes it clear which logger is being used and reduces naming collisions
- For components with very short files or temporary code, generic `log` is acceptable but descriptive is preferred

**Examples of good logger naming:**
**Examples of good logger declarations:**
```go
// Descriptive - clearly indicates the component (RECOMMENDED)
var logLauncher = logger.New("launcher:launcher")
var logPool = logger.New("launcher:pool")
var logConfig = logger.New("config:config")
var logValidation = logger.New("config:validation")
var logUnified = logger.New("server:unified")
var logRouted = logger.New("server:routed")

// Generic - acceptable for simple cases but less clear
var log = logger.New("auth:header")
var log = logger.New("sys:sys")
// Using ForFile() - namespace is auto-derived from file path (RECOMMENDED)
var logLauncher = logger.ForFile() // in launcher/launcher.go → "launcher:launcher"
var logValidation = logger.ForFile() // in config/validation.go → "config:validation"
var logUnified = logger.ForFile() // in server/unified.go → "server:unified"
var logRouted = logger.ForFile() // in server/routed.go → "server:routed"

// Using New() - only for intentionally custom namespaces
var logPool = logger.New("launcher:pool") // in connection_pool.go, custom shorter name
var logConfig = logger.New("config:config") // in config_core.go, intentional short name
```


Expand Down
14 changes: 10 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,9 @@ Use the logger package for debug logging:
```go
import "github.com/github/gh-aw-mcpg/internal/logger"

// Create a logger with namespace following pkg:filename convention
// Create a logger with namespace auto-derived from the calling file (PREFERRED)
// Use descriptive variable names (e.g., logLauncher, logConfig) for clarity
var logComponent = logger.New("pkg:filename")
var logComponent = logger.ForFile()

// Log debug messages (only shown when DEBUG environment variable matches)
logComponent.Printf("Processing %d items", count)
Expand All @@ -443,9 +443,15 @@ if logComponent.Enabled() {
}
```

`logger.ForFile()` automatically derives the namespace as `"package:filename"` from the calling
file path, eliminating manually maintained namespace strings and preventing drift.

Use `logger.New("pkg:component")` only when a custom namespace is intentionally different from
the file name (e.g., preserving a short backward-compatible debug namespace).

**Logger Variable Naming Convention:**
- **Prefer descriptive names**: `var log<Component> = logger.New("pkg:component")`
- Examples: `var logLauncher = logger.New("launcher:launcher")`
- **Prefer descriptive names**: `var log<Component> = logger.ForFile()`
- Examples: `var logLauncher = logger.ForFile()`, `var logHandlers = logger.ForFile()`
- Avoid generic `log` when it might conflict with standard library
- Capitalize the component part after 'log' (e.g., `logAuth` with capital 'A', `logLauncher` with capital 'L')

Expand Down
2 changes: 1 addition & 1 deletion internal/auth/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/util"
)

var logAuth = logger.New("auth:header")
var logAuth = logger.ForFile()
var logAPIKey = logger.New("auth:apikey")

var (
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/spf13/cobra"
)

var logProxyCmd = logger.New("cmd:proxy")
var logProxyCmd = logger.ForFile()

// Proxy subcommand flag variables
var (
Expand Down
2 changes: 1 addition & 1 deletion internal/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const (

// Package-level variables that don't belong to a specific feature
var (
debugLog = logger.New("cmd:root")
debugLog = logger.ForFile()
// cliVersion stores the version string for Cobra's CLI version display.
// This is kept separate from version.Get() because rootCmd.Version must be
// set at initialization time (before SetVersion is called). We sync both
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config_stdin.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/santhosh-tekuri/jsonschema/v6"
)

var logStdin = logger.New("config:config_stdin")
var logStdin = logger.ForFile()

// StdinConfig represents the JSON configuration format read from stdin.
type StdinConfig struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config_tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package config

import "github.com/github/gh-aw-mcpg/internal/logger"

var logTracingCfg = logger.New("config:config_tracing")
var logTracingCfg = logger.ForFile()

// DefaultTracingSampleRate is the default sample rate for tracing (100% sampling).
const DefaultTracingSampleRate = 1.0
Expand Down
2 changes: 1 addition & 1 deletion internal/config/guard_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logGuardPolicy = logger.New("config:guard_policy")
var logGuardPolicy = logger.ForFile()

const (
IntegrityNone = "none"
Expand Down
2 changes: 1 addition & 1 deletion internal/config/validation_env.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/sys"
)

var logEnv = logger.New("config:validation_env")
var logEnv = logger.ForFile()

// RequiredEnvVars lists the environment variables that must be set for the gateway to operate
var RequiredEnvVars = []string{
Expand Down
2 changes: 1 addition & 1 deletion internal/config/validation_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ var schemaErrPrinter = message.NewPrinter(language.English)

var (
// logSchema is the debug logger for schema validation
logSchema = logger.New("config:validation_schema")
logSchema = logger.ForFile()

// Schema caching to avoid recompiling the JSON schema on every validation.
// This improves performance by compiling the schema once and reusing it.
Expand Down
2 changes: 1 addition & 1 deletion internal/config/validation_shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logValidation = logger.New("config:validation_shared")
var logValidation = logger.ForFile()

// customSchemaCache stores compiled custom schemas by schema URL to avoid
// repeated fetch + compile work across validations.
Expand Down
2 changes: 1 addition & 1 deletion internal/difc/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/syncutil"
)

var logAgent = logger.New("difc:agent")
var logAgent = logger.ForFile()

// AgentLabels associates each agent with their DIFC labels
// Tracks what secrecy and integrity tags an agent has accumulated
Expand Down
2 changes: 1 addition & 1 deletion internal/difc/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package difc

import "github.com/github/gh-aw-mcpg/internal/logger"

var logCapabilities = logger.New("difc:capabilities")
var logCapabilities = logger.ForFile()

// Capabilities represents the global set of tags available in the system
// This is used to validate and discover available DIFC tags
Expand Down
2 changes: 1 addition & 1 deletion internal/difc/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logEvaluator = logger.New("difc:evaluator")
var logEvaluator = logger.ForFile()

// DIFC mode string constants - use these for consistent mode references
const (
Expand Down
2 changes: 1 addition & 1 deletion internal/difc/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logLabels = logger.New("difc:labels")
var logLabels = logger.ForFile()

// Tag represents a single DIFC tag (e.g., "repo:owner/name", "agent:demo-agent")
type Tag string
Expand Down
2 changes: 1 addition & 1 deletion internal/difc/path_labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/mcpresult"
)

var logPathLabels = logger.New("difc:path_labels")
var logPathLabels = logger.ForFile()

// PathLabels represents a collection of labeled paths in a JSON response.
// Guards return this structure to indicate which elements in the response
Expand Down
2 changes: 1 addition & 1 deletion internal/difc/pipeline_decisions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package difc

import "github.com/github/gh-aw-mcpg/internal/logger"

var logPipeline = logger.New("difc:pipeline_decisions")
var logPipeline = logger.ForFile()

// CoarseCheckOutcome is the typed result of a Phase 2 coarse-grained access check.
type CoarseCheckOutcome int
Expand Down
2 changes: 1 addition & 1 deletion internal/difc/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logReflect = logger.New("difc:reflect")
var logReflect = logger.ForFile()

// ReflectedAgentLabels is the JSON shape for an agent's current DIFC labels.
type ReflectedAgentLabels struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/difc/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package difc

import "github.com/github/gh-aw-mcpg/internal/logger"

var logResource = logger.New("difc:resource")
var logResource = logger.ForFile()

// Resource represents an external system with label requirements (deprecated - use LabeledResource)
type Resource struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/difc/sink_server_ids.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/util"
)

var logSink = logger.New("difc:sink_server_ids")
var logSink = logger.ForFile()

var (
sinkServerIDsMu sync.RWMutex
Expand Down
2 changes: 1 addition & 1 deletion internal/envutil/envfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/sanitize"
)

var logEnvFile = logger.New("envutil:envfile")
var logEnvFile = logger.ForFile()

// LoadEnvFile reads a .env file and sets environment variables.
// Lines beginning with '#' and blank lines are ignored.
Expand Down
2 changes: 1 addition & 1 deletion internal/envutil/envutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/sanitize"
)

var logEnvUtil = logger.New("envutil:envutil")
var logEnvUtil = logger.ForFile()

// HasEnvVar reports whether the named environment variable is present in the
// process environment, regardless of whether its value is empty.
Expand Down
2 changes: 1 addition & 1 deletion internal/envutil/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/sanitize"
)

var logGitHub = logger.New("envutil:github")
var logGitHub = logger.ForFile()

// DefaultGitHubAPIBaseURL is the default GitHub API base URL.
const DefaultGitHubAPIBaseURL = "https://api.github.com"
Expand Down
2 changes: 1 addition & 1 deletion internal/githubhttp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logHTTP = logger.New("githubhttp:client")
var logHTTP = logger.ForFile()

// GitHubUserAgent is the User-Agent header value sent on all GitHub API requests.
const GitHubUserAgent = "awmg/1.0"
Expand Down
2 changes: 1 addition & 1 deletion internal/githubhttp/collaborator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/util"
)

var logCollab = logger.New("githubhttp:collaborator")
var logCollab = logger.ForFile()

// ParseCollaboratorPermissionArgs extracts and validates the owner, repo, and
// username fields from an args map for a get_collaborator_permission call.
Expand Down
2 changes: 1 addition & 1 deletion internal/githubhttp/visibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logVisibility = logger.New("githubhttp:visibility")
var logVisibility = logger.ForFile()

// RepoVisibility represents the visibility of a GitHub repository.
type RepoVisibility string
Expand Down
2 changes: 1 addition & 1 deletion internal/guard/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logContext = logger.New("guard:context")
var logContext = logger.ForFile()

// ContextKey is used for storing values in context
type ContextKey string
Expand Down
2 changes: 1 addition & 1 deletion internal/guard/label_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logLabelAgent = logger.New("guard:label_agent")
var logLabelAgent = logger.ForFile()

// RunLabelAgentForAgent is a convenience wrapper around RunLabelAgent that resolves
// agent labels from the registry instead of requiring the caller to do so. It calls
Expand Down
2 changes: 1 addition & 1 deletion internal/guard/noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logNoop = logger.New("guard:noop")
var logNoop = logger.ForFile()

// NoopGuard is the default guard that performs no DIFC labeling
// It allows all operations by returning empty labels (no restrictions)
Expand Down
2 changes: 1 addition & 1 deletion internal/guard/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logPipeline = logger.New("guard:pipeline")
var logPipeline = logger.ForFile()

// PipelineInput holds the shared inputs used across DIFC pipeline phases 0–2, 4, and 6.
// Both the HTTP proxy and the MCP unified server populate this struct and pass it to
Expand Down
2 changes: 1 addition & 1 deletion internal/guard/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logRegistry = logger.New("guard:registry")
var logRegistry = logger.ForFile()

// Registry manages guard instances for different MCP servers
type Registry struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/httputil/httputil.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logHTTP = logger.New("httputil:httputil")
var logHTTP = logger.ForFile()

// WriteJSONResponse sets the Content-Type header, writes the status code, and encodes
// body as JSON. It centralises the three-line pattern used across HTTP handlers.
Expand Down
2 changes: 1 addition & 1 deletion internal/httputil/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/github/gh-aw-mcpg/internal/logger"
)

var logTLS = logger.New("httputil:tls")
var logTLS = logger.ForFile()

// MinTLSVersion is the minimum TLS version enforced across all gateway listeners
// and clients. Centralizing this constant ensures a single point of change if
Expand Down
Loading
Loading