Skip to main content

Hooks & Observability

radosgw-admin provides request lifecycle hooks that let you add logging, metrics, and telemetry without modifying the SDK internals.

How Hooks Work​

Hooks are callbacks configured when creating the client. They run on every HTTP request across all 8 modules — users, keys, subusers, buckets, quotas, rate limits, usage, and info.

import { RadosGWAdminClient } from 'radosgw-admin';

const rgw = new RadosGWAdminClient({
host: 'http://rgw:8080',
accessKey: '...',
secretKey: '...',
onBeforeRequest: [
(ctx) => console.log(`→ ${ctx.method} ${ctx.path}`),
],
onAfterResponse: [
(ctx) => console.log(`← ${ctx.status} in ${ctx.durationMs}ms`),
],
});

await rgw.users.list();
// → GET /user
// ← 200 in 45ms

Hook Context​

onBeforeRequest receives:​

FieldTypeDescription
method'GET' | 'POST' | 'PUT' | 'DELETE'HTTP method
pathstringRGW Admin API path (e.g. /user)
urlstringFull request URL with query params
queryRecord<string, ...>Query parameters (if any)
attemptnumber0-based retry attempt number
startTimenumberDate.now() when the request started

onAfterResponse receives all of the above, plus:​

FieldTypeDescription
statusnumber | undefinedHTTP status code (undefined for network errors)
durationMsnumberRequest duration in milliseconds
errorError | undefinedError if the request failed

Error Handling in Hooks​

Hooks never break RGW operations. If a hook throws, the error is silently caught and logged (when debug: true). The actual request/response continues normally.

onBeforeRequest: [
() => { throw new Error('hook crashed'); },
// ↑ swallowed — request still fires
],

Hooks and Retries​

When maxRetries > 0, hooks fire on every attempt, not just the first. The attempt field tells you which attempt it is:

onBeforeRequest: [
(ctx) => {
if (ctx.attempt > 0) {
console.warn(`Retry #${ctx.attempt} for ${ctx.method} ${ctx.path}`);
}
},
],

Examples​

Request Logging​

const rgw = new RadosGWAdminClient({
host: 'http://rgw:8080',
accessKey: '...',
secretKey: '...',
onBeforeRequest: [
(ctx) => console.log(`[RGW] → ${ctx.method} ${ctx.path}`),
],
onAfterResponse: [
(ctx) => {
const status = ctx.error ? `ERROR: ${ctx.error.message}` : ctx.status;
console.log(`[RGW] ← ${status} (${ctx.durationMs}ms)`);
},
],
});

Prometheus Metrics​

import { Histogram, Counter } from 'prom-client';

const requestDuration = new Histogram({
name: 'rgw_admin_request_duration_ms',
help: 'RGW Admin API request duration',
labelNames: ['method', 'path', 'status'],
});

const requestErrors = new Counter({
name: 'rgw_admin_request_errors_total',
help: 'RGW Admin API request errors',
labelNames: ['method', 'path'],
});

const rgw = new RadosGWAdminClient({
host: 'http://rgw:8080',
accessKey: '...',
secretKey: '...',
onAfterResponse: [
(ctx) => {
requestDuration.observe(
{ method: ctx.method, path: ctx.path, status: String(ctx.status ?? 'network_error') },
ctx.durationMs,
);
if (ctx.error) {
requestErrors.inc({ method: ctx.method, path: ctx.path });
}
},
],
});

Audit Logging​

import { appendFileSync } from 'node:fs';

const rgw = new RadosGWAdminClient({
host: 'http://rgw:8080',
accessKey: '...',
secretKey: '...',
onAfterResponse: [
(ctx) => {
const entry = JSON.stringify({
timestamp: new Date(ctx.startTime).toISOString(),
method: ctx.method,
path: ctx.path,
status: ctx.status,
durationMs: ctx.durationMs,
error: ctx.error?.message,
});
appendFileSync('/var/log/rgw-admin-audit.jsonl', entry + '\n');
},
],
});

Performance Monitoring (Slow Request Warning)​

const rgw = new RadosGWAdminClient({
host: 'http://rgw:8080',
accessKey: '...',
secretKey: '...',
onAfterResponse: [
(ctx) => {
if (ctx.durationMs > 2000) {
console.warn(
`[SLOW] ${ctx.method} ${ctx.path} took ${ctx.durationMs}ms`,
);
}
},
],
});

Security: Redacting Sensitive Data​

Hook context includes the full request URL. When creating users with pre-specified credentials, the URL may contain secret-key as a query parameter. If you send hook data to external systems, redact sensitive fields:

onAfterResponse: [
(ctx) => {
// Redact secret-key from URL before logging
const safeUrl = ctx.url.replace(/([?&]secret-key=)[^&]*/gi, '$1[REDACTED]');
logger.info({ method: ctx.method, url: safeUrl, status: ctx.status });
},
],

The SDK already redacts secret-key from its own debug logs, but hooks receive the raw URL so you have full control.

Non-Blocking Hooks​

If your hook sends data over the network (e.g. to a metrics server), avoid blocking RGW requests by not awaiting the network call:

onAfterResponse: [
(ctx) => {
// Fire-and-forget — don't await
metricsServer.send(ctx.durationMs).catch(() => {});
},
],

TypeScript Types​

All hook types are exported for consumers:

import type {
BeforeRequestHook,
AfterResponseHook,
HookContext,
} from 'radosgw-admin';