- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Logging That Helps at 3 AM: Structured Logs in Laravel
About Post
It's 3 AM. A tenant says their payment went through but the app shows it as unpaid. You open the logs and find this:
[2025-07-26 02:47:13] production.ERROR: Something went wrong
[2025-07-26 02:47:13] production.ERROR: Payment failed
[2025-07-26 02:47:14] production.INFO: here
Which payment? Which user? Which request? Is "here" related? You have logs, technically. What you don't have is information.
Good logging isn't about logging more. It's about writing each line for the tired person who'll search for it later. Usually that person is you.
Rule 1: the message is a label, the context is the data
Laravel's logger takes a second argument, a context array. Use it every time:
// Before
Log::error("Payment failed for invoice {$invoice->id}");
// After
Log::error('Payment gateway declined charge', [
'invoice_id' => $invoice->id,
'user_id' => $invoice->user_id,
'amount' => $invoice->amount,
'gateway_code' => $response->json('error.code'),
'attempt' => $attempt,
]);
Two things changed. The message is now a fixed string, so you can search for every occurrence of "Payment gateway declined charge" across all invoices. And the variable parts are separate fields, so a log tool can filter by invoice_id or count failures by gateway_code. That's what "structured" means.
Rule 2: give every request an ID
Under load, lines from twenty different requests interleave. A request ID lets you pull out exactly the lines that belong to one request. Laravel 11 introduced the Context facade, which makes this easy: anything you add to it is automatically attached to every log entry for the rest of the request.
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Str;
class AssignRequestId
{
public function handle(Request $request, Closure $next): Response
{
$incoming = $request->header('X-Request-Id');
$requestId = Str::isUuid($incoming) ? $incoming : (string) Str::uuid();
Context::add('request_id', $requestId);
$response = $next($request);
$response->headers->set('X-Request-Id', $requestId);
return $response;
}
}
Register it as global middleware in bootstrap/app.php with $middleware->prepend(AssignRequestId::class). Returning the ID in a response header is a small, lovely bonus: when a user reports an error, support can ask for the ID (or your mobile app can show it on the error screen) and you go straight to the right lines.
The best part: Context data also travels into queued jobs dispatched during that request. So the job that sends the receipt email logs with the same request_id as the request that created the payment. Add the user ID too, once they're authenticated, and every line answers "who?" for free.
Rule 3: levels are a promise about urgency
If everything is an error, nothing is. I think of levels as "who needs to care, and how soon":
| Level | Meaning | Example |
|---|---|---|
debug | Useful while developing, off in production | Query timings, payload shapes |
info | Normal business events worth a record | Contract signed, payment received |
warning | Something odd, handled, worth watching | Retrying a gateway call, slow response |
error | Something failed and a person may need to act | Payment callback couldn't be processed |
critical | Wake someone up | Can't reach the database, queue is stuck |
A validation failure is not an error. A user typing a wrong password is not an error. If they show up as errors, real errors drown, and alerts become noise people learn to ignore.
Rule 4: log the decision, not the trail
"Entered function", "here", "here 2": these were useful for five minutes while debugging and useless forever after. The lines worth keeping record decisions and outcomes: why a payment was rejected, why a job skipped a record, which branch a webhook took and why.
A good test: if this line appeared in an incident, would it tell you something you couldn't get from the database?
Rule 5: never log secrets (it's easier than you think)
The most common leak isn't someone logging a password on purpose. It's logging a whole request or response "just to see it":
// Risky: may contain passwords, tokens, card details
Log::info('Incoming request', $request->all());
// Better: pick what you need
Log::info('Profile update requested', $request->only(['name', 'phone']));
Watch out for Authorization headers, API responses that echo tokens back, and full model arrays ($hidden on a model only affects serialisation, so think before dumping attributes). For stack traces, PHP 8.2's #[\SensitiveParameter] attribute redacts a parameter's value from backtraces.
The 3 AM test: before you write a log line, ask "what will I search for when this goes wrong?" Put that in the context array. If the answer is "nothing", don't write the line.
Rule 6: write JSON when a machine will read it
Plain text is fine when you read logs with tail. Once logs go to CloudWatch or any log platform, JSON lets you query fields directly. A custom channel in config/logging.php:
'json' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'info'),
'handler' => Monolog\Handler\StreamHandler::class,
'handler_with' => ['stream' => storage_path('logs/laravel.json')],
'formatter' => Monolog\Formatter\JsonFormatter::class,
],
Combine channels with the stack driver: JSON to a file or stderr for everything, plus a Slack or email channel at critical level only. The logging docs cover all the drivers.
Bonus: let exceptions carry their own context
Custom exceptions in Laravel can define a context() method that returns an array. Laravel adds it to the log entry when the exception is reported, so a PaymentDeclined exception can always log its invoice and gateway code without every catch block remembering to.
Checklist
- Fixed messages, variable data in the context array.
- A request ID on every line, via middleware and
Context, returned in a header. - Levels that mean something; errors are for things a person must act on.
- Decisions and outcomes, not breadcrumbs.
- No secrets, no full request dumps.
- JSON once a machine reads your logs.
What's the worst log line you've ever had to debug with? I've seen plenty of "here" over the years, and at least once I'm fairly sure I wrote it.

Be first to comment it...