
Best Practices for Application Activity Logging. A Practical Guide for Node.js and Go Developers

When building modern applications, it’s easy to overlook logging — until something breaks and you spend hours debugging or a security incident happens. But logging isn’t just about debugging. Done right, it’s a powerful tool for monitoring, security, and compliance.
In this guide, you’ll learn why activity logging matters, what to log, and how to implement robust logging in your Node.js (Fastify/NestJS) or Golang projects using tools like Pino and slog.
🔑 Key Takeaways
- Log essential details: When, Where, Who, and What.
- Use structured logs (like JSON) for easier analysis.
- Prefer efficient loggers: Pino (Node.js) and slog (Golang).
- Avoid logging sensitive data and protect against log injection.
- Logging is crucial for security, operations, and compliance.
🚨 Why Logging Matters
Logging helps you:
- Detect security issues: suspicious login attempts, data access violations.
- Debug problems: trace errors and performance issues.
- Comply with regulations: maintain audit trails for GDPR, PCI DSS, etc.
Without logs, you’re flying blind — especially in production.
🧩 What Events Should You Log?
Not every event needs to be logged — too much noise can hide real issues. Focus on events that are essential for security, troubleshooting, and compliance:
🔐 Security & Access
- Input/output validation failures
- Auth successes & failures
- Authorization denials
- Session issues (e.g. token tampering, invalid JWTs)
- Deserialization errors
- TLS or HTTP protocol violations
⚙️ System & App Behavior
- App start, stop, restart
- Runtime errors, timeouts, failed dependencies
- Config changes or failed file operations
🛡️ High-Risk Actions
- Admin/user privilege changes
- Sensitive data access
- Encryption key use or rotation
- File uploads and virus scan results
🚩 Suspicious Patterns
- Bypassing workflows or exceeding usage limits
- Unexpected sequences or failed business logic checks
- Fraud attempts or abnormal access patterns
Only log what helps your team detect, respond, or audit effectively — and always sanitize inputs before logging to avoid injection risks.
📋 What to Log: The 4Ws
Every log entry should answer these questions:
Field | Example |
---|---|
When | 2025-05-29T16:22:00Z |
Where | service=user-api , IP=192.168.1.1 |
Who | userId=123 , sessionId=abc123 |
What | action=login_failed , error=InvalidPassword |
Keep it consistent and structured, especially across services.
🔧 How to Log in Practice
There a lot available logger solutions in Node.js world, but I prefer pino.
Node.js with Pino
Pino is a high-performance, low-overhead logging library for Node.js, built to be faster and more efficient than alternatives like Winston or Bunyan. It outputs logs in JSON format by default, so you don’t need to worry about extra formatting or processing.
✅ Fastify example
In the case of the Fastify framework, Pino is built-in by default, so you get high-performance logging out of the box without any extra setup.
const Fastify = require('fastify');
const app = Fastify({ logger: true });
app.get('/', (req, reply) => {
req.log.info('Root endpoint hit');
reply.send({ status: 'ok' });
});
app.listen({ port: 3000 });
✅ NestJS example (with** nestjs-pino)
For NestJS, you need to set up Pino using the nestjs-pino package, but the improved performance and structured logs make it totally worth it.
import { Module } from '@nestjs/common';
import { LoggerModule } from 'nestjs-pino';
@Module({
imports: [
LoggerModule.forRoot({
pinoHttp: {
transport: {
target: 'pino-pretty',
},
},
}),
],
})
export class AppModule {}
Use it in services:
import { Logger } from '@nestjs/common';
@Injectable()
export class MyService {
private readonly logger = new Logger(MyService.name);
doStuff() {
this.logger.log('Doing something');
}
}
Pino is highly customizable and supports a variety of transports for logging—such as writing to files, sending logs to third-party services, and more.) for logging—such as writing to files, sending logs to third-party services, and more.
Golang with slog
Go’s standard log is simple but limited. Use Go’s log/slog
package, introduced in Go 1.21 as part of standard library, to perform structured logging. The JSONHandler outputs logs in JSON format, which is ideal for integration with log aggregation tools and for maintaining readable, structured logs.
✅ Basic slog example
package main
import (
"log/slog"
"os"
)
func main() {
// Create a JSON handler that writes to standard output
handler := slog.NewJSONHandler(os.Stdout, nil)
// Initialize a new logger with the JSON handler
logger := slog.New(handler)
// Log an informational message with structured attributes
logger.Info("Application started",
slog.String("module", "main"),
slog.Int("port", 8080),
)
}
Key Points:
- Structured Logging: By using key-value pairs, logs become more parseable and easier to analyze.
- Handlers: slog provides different handlers like JSONHandler and TextHandler to format logs as needed.
- Attributes: Using slog.String, slog.Int, etc., ensures type safety and clarity in logs.
- Standard Library: slog is a part of standard library
🚀 Advanced Logging Techniques
- Structured Logging: JSON logs work best with log aggregators (e.g., ELK, Loki).
- Log Enrichment: Include userId, requestId, traceId, etc.
- Custom Levels: Use debug, info, warn, error, and create custom levels if needed.
- Flexible Destinations: Log to stdout, files, cloud log services, or message queues.
⚠️ Common Logging Mistakes to Avoid
- ❌ Logging sensitive data (e.g., passwords, tokens)
- ❌ Ignoring log injection risks (sanitize input before logging)
- ❌ Using console.log in production
- ❌ Storing logs forever — set retention policies
✅ Conclusion
Logging is a foundation of secure and maintainable software. By:
- Logging the right data (When, Where, Who, What),
- Using structured formats (JSON),
- Leveraging efficient tools (Pino for Node.js, slog for Go),
Want to make your logs actionable? Start small — log only what matters, use structured formats, and gradually build a logging strategy that scales with your app. And don’t forget: logs are only useful if someone (or something) is reading them.