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';