Logging Monitoring Failures in Feathersjs with Cockroachdb
Logging Monitoring Failures in Feathersjs with Cockroachdb — how this specific combination creates or exposes the vulnerability
When Feathersjs services interact with Cockroachdb, logging and monitoring gaps can leave failures undetected or improperly recorded, increasing operational and security risk. Feathersjs relies on structured logging and hooks for observability, but if log levels are not explicitly set or if errors from Cockroachdb are not fully captured, important signals are lost. Cockroachdb returns specific error codes and transaction retry guidance that must be logged with sufficient context to be actionable. Without consistent log collection and correlation, incidents such as serialization failures, leaseholder changes, or network partitions may appear as generic 500 errors, obscuring root cause. Instrumentation that traces request IDs across Feathersjs services and into Cockroachdb queries is essential; missing trace identifiers in logs make it difficult to reconstruct transaction sequences during an incident. In addition, if query retries or transaction aborts are not surfaced in logs and metrics, operators may miss patterns that indicate contention or faulty client logic. These gaps are especially pronounced when Cockroachdb is run in multi-region configurations, where latency and retry behavior vary. Feathersjs hooks that do not log query parameters, affected row counts, or transaction states reduce visibility into data integrity issues. Monitoring systems that do not ingest structured logs and query metrics from Cockroachdb may fail to alert on abnormal error rates or prolonged transaction durations. The combination of Feathersjs event-driven patterns and Cockroachdb’s distributed SQL semantics amplifies these risks when logging is inconsistent or incomplete.
Cockroachdb-Specific Remediation in Feathersjs — concrete code fixes
Implement robust logging and monitoring around Cockroachdb operations in Feathersjs to ensure failures are recorded with sufficient context. Use Feathersjs hooks to intercept service methods and enrich logs with query metadata, transaction IDs, and retry information. Configure structured logging with consistent fields such as timestamp, level, service, requestId, transactionId, sql, parameters, and error details. Below are concrete examples that demonstrate these practices.
Structured logging with a Feathersjs hook
// src/hooks/logging.js
const { v4: uuidv4 } = require('uuid');
function loggingHook(options = {}) {
return async context => {
const requestId = context.params.headers['x-request-id'] || uuidv4();
const transactionId = uuidv4();
const start = Date.now();
// Attach identifiers to context for downstream use
context.params.requestId = requestId;
context.params.transactionId = transactionId;
const { service, method, data, query } = context;
const logBase = {
timestamp: new Date().toISOString(),
level: 'info',
service: service.name,
method,
requestId,
transactionId,
query,
dataKeys: Array.isArray(data) ? data.map(d => d.id || d).join(',') : (data && data.id) || null
};
try {
const result = await context;
const duration = Date.now() - start;
Object.assign(logBase, {
level: 'info',
resultCount: Array.isArray(result) ? result.length : (result ? 1 : 0),
duration
});
console.log('feathers-db-operation', logBase);
return result;
} catch (error) {
const duration = Date.now() - start;
const cockroachError = error.meta && error.meta.cockroachError;
Object.assign(logBase, {
level: 'error',
error: error.message,
stack: error.stack,
name: error.name,
duration,
cockroachError: cockroachError ? {
code: cockroachError.code,
severity: cockroachError.severity,
detail: cockroachError.detail,
hint: cockroachError.hint
} : null,
transactionState: error.transactionState
});
console.error('feathers-db-error', logBase);
throw error;
}
};
}
module.exports = function () {
return loggingHook;
};
Instrumenting a Feathersjs service with retry-aware logging for Cockroachdb
// src/services/orders/orders.class.js
const { Service } = require('feathersjs-sequelize');
const { Pool } = require('pg'); // Cockroachdb wire protocol compatible
class OrdersService extends Service {
constructor(options) {
super(options);
this.pool = new Pool({
connectionString: process.env.DATABASE_URL,
application_name: 'feathers-orders',
// Cockroachdb-specific settings
preferQueryMode: 'simple',
ssl: { rejectUnauthorized: false }
});
}
async create(data, params) {
const { requestId, transactionId } = params;
const client = await this.pool.connect();
let retries = 0;
const maxRetries = 3;
while (retries <= maxRetries) {
try {
await client.query('BEGIN');
const result = await client.query(
`INSERT INTO orders (user_id, total, status) VALUES ($1, $2, $3) RETURNING id, created_at`,
[data.userId, data.total, data.status]
);
await client.query('COMMIT');
this.logOperation({
requestId,
transactionId,
sql: 'INSERT INTO orders',
params: [data.userId, data.total, data.status],
duration: 0, // compute as needed
rowsAffected: result.rowCount
});
return result.rows[0];
} catch (error) {
await client.query('ROLLBACK').catch(() => {});
const isRetriable = this.isRetriableCockroachError(error);
this.logOperation({
requestId,
transactionId,
sql: 'INSERT INTO orders',
params: [data.userId, data.total, data.status],
error: error.message,
cockroachCode: error.code,
duration: 0,
retryAttempt: retries,
retriable: isRetriable
});
if (!isRetriable || retries >= maxRetries) {
throw error;
}
retries += 1;
// Exponential backoff
await new Promise(res => setTimeout(res, 100 * Math.pow(2, retries)));
} finally {
client.release();
}
}
}
isRetriableCockroachError(error) {
if (!error || !error.code) return false;
// Cockroachdb retryable error codes: 40001 serialization, 23000 unique_violation may be retried with caution
const retryableCodes = ['40001', '23000', '08006', '08001'];
return retryableCodes.includes(error.code);
}
logOperation({ requestId, transactionId, sql, params, error, cockroachCode, duration, rowsAffected, retryAttempt, retriable }) {
const entry = {
timestamp: new Date().toISOString(),
level: error ? 'error' : 'info',
service: 'orders',
requestId,
transactionId,
sql,
params,
duration,
rowsAffected: rowsAffected || 0,
retryAttempt: retryAttempt || 0,
retriable: retriable || false,
cockroachCode,
error: error ? { message: error.message, name: error.name } : null
};
// Use structured output compatible with log shippers
console[error ? 'error' : 'info'](JSON.stringify(entry));
}
}
module.exports = function () {
return new OrdersService();
};
Correlation and monitoring integration
Ensure each Feathersjs request carries a request ID that is propagated to Cockroachdb session context when supported, enabling query-level tracing. Forward structured logs to a centralized system and tag Cockroachdb metrics (e.g., query latency, transaction aborts, retries) with service and method names. Alert on high rates of retriable Cockroachdb errors or long-running transactions to detect contention early.