Help us improve
Share bugs, ideas, or general feedback.
From code-audit
Audits logging quality across a codebase, identifying missing log statements, incorrect severity levels, and poor message quality. Produces a prioritized recommendation report without editing files.
npx claudepluginhub dan323/easier-life-skills --plugin code-auditHow this skill is triggered — by the user, by Claude, or both
Slash command
/code-audit:improve-logginghaikuThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Audit a codebase's logging and produce a prioritized list of recommendations covering three areas: missing log statements, incorrect severity levels, and poor message quality. Also enforces (or proposes) a consistent logging pattern across the whole application.
Provides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.
Share bugs, ideas, or general feedback.
Audit a codebase's logging and produce a prioritized list of recommendations covering three areas: missing log statements, incorrect severity levels, and poor message quality. Also enforces (or proposes) a consistent logging pattern across the whole application.
This skill is read-only. It produces a recommendation report. It does not edit any file.
Before doing any work, call TaskCreate for each phase below. Call TaskUpdate (status in_progress) when you begin a phase and TaskUpdate (status completed) when you finish it.
# Identify languages
find . -maxdepth 4 -not -path "*/.git/*" -not -path "*/node_modules/*" \
-not -path "*/vendor/*" -not -path "*/.venv/*" \
\( -name "*.py" -o -name "*.java" -o -name "*.kt" -o -name "*.ts" \
-o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.go" \
-o -name "*.rs" -o -name "*.cs" -o -name "*.rb" \) \
| sed 's/.*\.//' | sort | uniq -c | sort -rn | head -10
Detect the logging framework in use per language:
| Language | Frameworks to detect |
|---|---|
| Python | import logging, from loguru, import structlog |
| Java/Kotlin | import org.slf4j, import org.apache.logging, import io.github.oshai |
| TypeScript/JS | import winston, import pino, console.log/warn/error |
| Go | import "log", "go.uber.org/zap", "github.com/sirupsen/logrus" |
| C# | ILogger, Microsoft.Extensions.Logging, Serilog, NLog |
| Ruby | Rails.logger, Logger.new |
# Python
grep -rn "import logging\|from loguru\|import structlog" --include="*.py" . | head -5
# Java/Kotlin
grep -rn "import org.slf4j\|import org.apache.logging\|LoggerFactory\|KotlinLogging" \
--include="*.java" --include="*.kt" . | head -5
# TypeScript/JS
grep -rn "require.*winston\|require.*pino\|import.*winston\|import.*pino\|console\.\(log\|warn\|error\|info\|debug\)" \
--include="*.ts" --include="*.js" --include="*.tsx" --include="*.jsx" . | head -5
# Go
grep -rn '"log"\|"go.uber.org/zap"\|"github.com/sirupsen/logrus"\|"golang.org/x/exp/slog"' \
--include="*.go" . | head -5
Sample up to 20 existing log call sites to understand the current pattern (or absence of one):
# Python example — find log calls
grep -rn "logger\.\|logging\." --include="*.py" . | head -20
# Java/Kotlin example
grep -rn "log\.\(info\|debug\|warn\|error\|trace\)" --include="*.java" --include="*.kt" . | head -20
# Go example
grep -rn "log\.\|logger\.\|zap\.\|logrus\." --include="*.go" . | head -20
Read 3–5 representative files where logging is present to understand:
log.Info("done", "user_id", id))? Which fields appear consistently?Every log call must include at minimum:
| Field | Purpose | When required |
|---|---|---|
| Event name | What happened (the message) | Always |
request_id / trace_id | Correlate logs across a request | Any code that handles an incoming request (HTTP, gRPC, queue consumer) |
user_id | Who triggered the action | Any user-facing operation where a user ID is in scope |
error / err | The error value or message | Any log at WARN or ERROR level |
If the codebase already uses these fields consistently, document them as required. If not, include them in the proposed pattern. Flag any log call that is in request-scoped code but omits request_id / trace_id as an inconsistency (Phase 3d).
Recommended patterns by framework:
Python (structlog or logging):
log = structlog.get_logger(__name__)
log.info("order.placed", order_id=order_id, user_id=user_id)
# Message: noun.verb snake_case, context as keyword args
Go (zap or slog):
logger.Info("order placed", zap.String("order_id", orderID), zap.String("user_id", userID))
// Message: lowercase prose, context as typed fields
Java/Kotlin (SLF4J):
log.info("Order placed: orderId={}, userId={}", orderId, userId);
// Message: sentence case prose, context via SLF4J placeholders
TypeScript/JS (pino or winston):
logger.info({ orderId, userId }, 'Order placed')
// pino style: context object first, message second
Look for code paths that almost certainly should be logged but aren't. Focus on high-value locations:
Error handlers with no log:
# Python
grep -rn "except " --include="*.py" -A 3 . | grep -v "log\|logger\|print" | head -20
# Java
grep -rn "catch\s*(" --include="*.java" -A 3 . | grep -v "log\.\|LOG\.\|logger\." | head -20
# Go
grep -rn "if err != nil" --include="*.go" -A 2 . | grep -v "log\.\|logger\." | head -20
# TypeScript
grep -rn "catch\s*(" --include="*.ts" --include="*.js" -A 3 . | grep -v "console\.\|logger\.\|log\." | head -20
Important operations with no surrounding log — use these grep patterns to find high-value functions, then read them to check whether they log:
# Functions whose names signal state changes or side effects
grep -rn "def \(create\|update\|delete\|remove\|send\|publish\|process\|handle\|execute\|run\|dispatch\)_" \
--include="*.py" .
grep -rn "func \(create\|update\|delete\|remove\|send\|publish\|process\|handle\|execute\|run\|dispatch\)[A-Z]" \
--include="*.go" .
grep -rn "^\s*\(public\|private\|protected\).*\(create\|update\|delete\|send\|process\|handle\)[A-Z]" \
--include="*.java" --include="*.kt" .
grep -rn "^\(export \)\?\(async \)\?function \(create\|update\|delete\|send\|process\|handle\)" \
--include="*.ts" --include="*.js" .
Also check these locations regardless of name:
handlers/, controllers/, routes/, views/)jobs/, workers/, tasks/, cron/)init(), signal handlers)Flag an operation as missing a log if:
For each log call found, evaluate whether the level matches the situation. Apply these rules:
| Level | When to use | Common mistakes |
|---|---|---|
DEBUG | Implementation details useful during development; disabled in production | Using DEBUG for events that operators need to see in prod |
INFO | Normal, expected application flow; key business events | Logging every row in a loop as INFO; using INFO for recoverable errors |
WARN | Something unexpected happened but the application recovered; a condition that may cause problems later | Using WARN for actual errors; using WARN for routine things |
ERROR | An operation failed and requires attention; may affect a user or data | Swallowing errors silently; logging errors as WARN |
FATAL / CRITICAL | Application cannot continue; imminent shutdown | Overusing FATAL for recoverable errors |
Exception handling rules — apply these precisely when reading error-handling paths:
| Situation | Correct level | Rationale |
|---|---|---|
| Exception caught and re-raised | ERROR | The operation failed; the caller sees an exception. Log here so there is at least one record even if callers don't. |
| Exception caught and handled (request still succeeds) | WARN | Something unexpected happened but was recovered from. |
| Exception caught and swallowed (no re-raise, no response change) | Flag as missing log | Silent swallowing hides failures entirely. Always log before swallowing. |
| Expected, routine condition (e.g. cache miss, 404 lookup) | DEBUG or no log | Not an error; high-volume events at WARN/ERROR pollute alerting. |
HTTP status code → log level mapping — when code sets or returns an HTTP status, the log level should follow:
| Status range | Log level | Rationale |
|---|---|---|
| 2xx | INFO (or no log if framework logs requests) | Success — normal flow |
| 3xx | DEBUG | Redirect — expected, low value |
| 4xx (client error) | WARN | Client made a bad request; server is fine |
| 5xx (server error) | ERROR | Server failed; needs attention |
# HTTP responses logged at wrong level (Python/Flask/FastAPI)
grep -rn "status_code\s*=\s*[45][0-9][0-9]\|return.*[45][0-9][0-9]" --include="*.py" -A 3 . | \
grep "logger\.\(debug\|info\|warning\)"
# 5xx logged at warn instead of error (JS/TS)
grep -rn "res\.status(5[0-9][0-9])" --include="*.ts" --include="*.js" -A 5 . | \
grep "console\.warn\|logger\.warn"
# 4xx logged at error (over-alerting)
grep -rn "res\.status(4[0-9][0-9])" --include="*.ts" --include="*.js" -A 5 . | \
grep "console\.error\|logger\.error"
Also scan for:
# Errors logged at INFO or WARN (Python)
grep -rn "logger\.info\|logging\.info\|logger\.warning" --include="*.py" . | grep -i "error\|fail\|exception\|crash"
# Exceptions caught and logged at DEBUG
grep -rn "\.debug(" --include="*.java" --include="*.kt" . | grep -i "exception\|error\|fail"
# console.log used for errors in JS/TS
grep -rn "console\.log" --include="*.ts" --include="*.js" . | grep -i "error\|fail\|exception"
Read through error-handling paths in representative files — grep catches obvious mismatches, but reading reveals the subtler ones (e.g. a re-raise with no preceding log).
Scan for log calls that should be deleted entirely rather than improved. Flag all matches — these are the highest-priority recommendations because sensitive data in logs is a security incident waiting to happen.
Sensitive data:
# Common sensitive field names in log arguments
grep -rn "password\|passwd\|secret\|api_key\|apikey\|token\|credential\|ssn\|credit_card\|cvv\|private_key" \
--include="*.py" --include="*.java" --include="*.ts" --include="*.js" --include="*.go" --include="*.rb" . \
| grep -i "log\|logger\|print\|console" | head -30
Flag any log call that passes a sensitive field as a parameter or includes it in the message string. Include a clear explanation of the security risk (e.g. "API keys in logs are stored in plain text and accessible to anyone with log access, including log aggregation services").
Debug artifacts: print() in Python, console.log() / console.debug() in JS/TS, fmt.Println() in Go, System.out.println() in Java — these are almost never intentional in production code. Flag all of them.
Log spam: log calls inside tight loops (for/while) that fire on every iteration. A single INFO log per item in a large batch creates millions of log entries. Suggest replacing with a summary log after the loop (e.g. "Processed N items in Xms").
Redundant logs: multiple log calls at the same level for the same event within a few lines, where one clearly subsumes the other.
A good log message answers: what happened, in what context, and why does it matter?
Flag messages that:
"Error", "Something went wrong", "Failed", "null", "done", "ok""User not found" when the user ID is in scopelog.error("NullPointerException", e) adds nothing# Vague messages (Python)
grep -rn "logger\.\w\+(['\"].\{1,20\}['\"])" --include="*.py" . | \
grep -i '"error"\|"fail"\|"exception"\|"done"\|"ok"\|"success"\|"null"'
# Very short messages (Java)
grep -rn 'log\.\w\+("\w\{1,10\}")' --include="*.java" . | head -20
For files with message quality issues, read the surrounding code to suggest a better message that includes the relevant context variables.
Compare each log call against the pattern established in Phase 2:
request_id, or a WARN/ERROR log that omits error/errGroup all findings into a single report. Order: Remove → Missing logs → Wrong level → Poor messages → Inconsistencies.
Put Remove first because sensitive data in logs is an immediate security risk — it must be addressed before any other improvement.
Within each section, order by file. For each finding include the file path, line number, the current code (if any), and a concrete suggested improvement.
Use this format:
## Logging Improvement Report
### Established Pattern
[State the detected or proposed pattern here. If proposed, prefix with "⚡ No consistent pattern found — proposing:"]
Example:
logger.info("order.placed", order_id=order_id, amount=amount)
# noun.verb message, structured keyword args, module-level logger
---
### 1. Remove (N findings)
**src/auth/login.py:14** — sensitive field in log
Current: `logger.info("login success", extra={"user_id": user.id, "password": password})`
Remove: the `password` field from the extra dict
Why: passwords in logs are stored in plain text and accessible to everyone with log access, including log aggregation services
**src/debug/util.py:8** — debug artifact
Current: `print(f"Login attempt for {username}")`
Action: Replace with `logger.info("auth.login_attempt", username=username)` or remove
Why: print() bypasses the logging framework and cannot be filtered or silenced in production
---
### 2. Missing Log Statements (N findings)
**src/payments/processor.py:88** — error path with no log
Context: `except StripeError as e:`
Add: `logger.error("payment.charge_failed", order_id=order_id, error=str(e))`
Why: stripe failures are silent — operators have no visibility when charges fail
**src/jobs/email_sender.py:34** — external call with no failure log
Context: `response = smtp.send(msg)`
Add (on failure): `logger.error("email.send_failed", recipient=msg.to, error=str(e))`
---
### 3. Incorrect Log Levels (N findings)
**src/auth/login.py:52** — ERROR logged as WARNING
Current: `logger.warning("Login failed for user %s", user_id)`
Suggest: `logger.error("auth.login_failed", user_id=user_id)`
Why: a failed login attempt is an error worth alerting on, not just a warning
**src/cache/client.py:19** — DEBUG in a tight loop producing log spam
Current: `logger.debug("Cache miss for key %s", key)` (called on every request)
Suggest: Remove or gate behind a feature flag; emit a summary metric instead
---
### 4. Poor Message Quality (N findings)
**src/api/users.py:101**
Current: `logger.error("Error")`
Suggest: `logger.error("user.fetch_failed", user_id=user_id, error=str(e))`
Why: "Error" is unactionable — which user, which operation, what went wrong?
**src/db/connection.py:14**
Current: `logger.info("Connected")`
Suggest: `logger.info("db.connected", host=host, port=port, database=db_name)`
Why: context needed for debugging connection issues across environments
---
### 5. Inconsistencies (N findings)
**src/orders/service.py:67** — unstructured message where structured is the norm
Current: `logger.info(f"Order {order_id} placed by {user_id}")`
Suggest: `logger.info("order.placed", order_id=order_id, user_id=user_id)`
---
### Summary
| Category | Count |
|-----------------------|-------|
| Remove | 2 |
| Missing log statements| 4 |
| Incorrect log levels | 3 |
| Poor message quality | 6 |
| Inconsistencies | 5 |
| **Total** |**20** |
### Recommended next steps
- Address "Remove" immediately — sensitive data in logs is a security incident waiting to happen.
- Address "Missing log statements" next — silent error paths are the highest operational risk.
- Apply the established pattern to all new log calls going forward.
- Consider adding a linting rule to enforce log level discipline in CI.
If no issues are found in a section, omit that section entirely rather than writing "No issues found."
If the codebase has fewer than 5 log calls total, lead with the pattern proposal and focus the report on where to add logging rather than what to fix.