Observability & Logging Guidelines β
Welcome to the Agri Backend observability guide! π
Our application logs and metrics are sent to Elasticsearch and visualized in Kibana. We also use Elastic APM for distributed tracing and request correlation.
When working locally your logs & metrics can also be searchable. Sets the relevant environment variables and NODE_ENV=local.
Logging with NestJS β
- Use the custom logger (
MyLogger) in your NestJS services/components:
@Injectable()
export class SomeService {
private readonly logger: MyLogger = new MyLogger(SomeService.name);
constructor() {}
async doSomething(input: any) {
const result = await this.compute(input);
this.logger.log(
{
savedTimeMs: result,
input: {
questionsCpt: questionDocs.length,
charactersCpt: characterDocs.length,
},
},
`Time saved computed: ${JSON.stringify(result)}`,
);
}
}- Context attributes (the first argument to
log) are indexed and searchable in Elasticsearch/Kibana. - The log message (second argument) is indexed as a string.
Using logger β
- Always inject
MyLoggervia the constructor:
private readonly logger = new MyLogger(Service.name)
constructor() {}- Add relevant context to your logs for better searchability and debugging.
- Don't be crazy with context. ES is a paid service even if it can handle a lot we should try to avoid putting huge data there
Adding log context / searchable fields β
Log context allows you to add structured metadata to your logs, making them more searchable and informative in the monitoring dashboard.
How to Add Context β
When logging, you can pass an optional context object as the second argument. This object's attributes become indexed and searchable in Elasticsearch/Kibana.
// Basic logging with context
this.logger.log('User logged in', {
userId: user.id,
email: user.email,
loginMethod: 'email',
ipAddress: req.ip,
});
// Logging with error context
this.logger.error('Database connection failed', {
errorCode: 'DB_CONN_001',
connectionString: 'mongodb://...',
retryAttempt: 3,
});Best Practices for Log Context β
- Use Primitive Types: Stick to simple types like strings, numbers, and booleans
- Keep it Concise: Don't log massive objects or sensitive information
- Be Consistent: Use similar key names across different log statements
- Include Relevant Details: Add context that helps in debugging or understanding the system state
Searchable Context Examples β
- User-related logs:
userId,email,role - Performance metrics:
executionTimeMs,resourceUsed - System events:
serviceVersion,environmentName - Error tracking:
errorCode,stackTrace
What to Avoid β
- Avoid logging sensitive data (passwords, tokens)
- Don't log entire large objects
- Keep the context object relatively small and meaningful
// β
Good: Concise, relevant context
this.logger.log('Processing user request', {
requestId: uuid(),
endpoint: '/api/users',
});
// β Bad: Too much information, potential security risk
this.logger.log('User details', {
user: JSON.stringify(userObject), // Avoid logging entire objects
sensitiveData: user.password, // Never log sensitive information
});Performance Considerations β
While Elasticsearch can handle a lot of data, be mindful of the volume and complexity of your log contexts to manage costs and performance.