Set up Apollo.io monitoring and observability. Use when implementing logging, metrics, tracing, and alerting for Apollo integrations. Trigger with phrases like "apollo monitoring", "apollo metrics", "apollo observability", "apollo logging", "apollo alerts".
Comprehensive observability setup for Apollo.io integrations including metrics, logging, tracing, and alerting.
// src/lib/apollo/metrics.ts
import { Registry, Counter, Histogram, Gauge } from 'prom-client';
const register = new Registry();
// Request metrics
export const apolloRequestsTotal = new Counter({
name: 'apollo_requests_total',
help: 'Total number of Apollo API requests',
labelNames: ['endpoint', 'method', 'status'],
registers: [register],
});
export const apolloRequestDuration = new Histogram({
name: 'apollo_request_duration_seconds',
help: 'Duration of Apollo API requests in seconds',
labelNames: ['endpoint', 'method'],
buckets: [0.1, 0.25, 0.5, 1, 2.5, 5, 10],
registers: [register],
});
// Rate limit metrics
export const apolloRateLimitRemaining = new Gauge({
name: 'apollo_rate_limit_remaining',
help: 'Remaining Apollo API rate limit',
labelNames: ['endpoint'],
registers: [register],
});
export const apolloRateLimitHits = new Counter({
name: 'apollo_rate_limit_hits_total',
help: 'Number of times rate limit was hit',
registers: [register],
});
// Cache metrics
export const apolloCacheHits = new Counter({
name: 'apollo_cache_hits_total',
help: 'Number of Apollo cache hits',
labelNames: ['endpoint'],
registers: [register],
});
export const apolloCacheMisses = new Counter({
name: 'apollo_cache_misses_total',
help: 'Number of Apollo cache misses',
labelNames: ['endpoint'],
registers: [register],
});
// Credit usage
export const apolloCreditsUsed = new Counter({
name: 'apollo_credits_used_total',
help: 'Total Apollo credits consumed',
labelNames: ['operation'],
registers: [register],
});
// Error tracking
export const apolloErrors = new Counter({
name: 'apollo_errors_total',
help: 'Total Apollo API errors',
labelNames: ['endpoint', 'error_type'],
registers: [register],
});
export { register };
// src/lib/apollo/instrumented-client.ts
import { apolloRequestsTotal, apolloRequestDuration, apolloErrors } from './metrics';
export class InstrumentedApolloClient {
async request<T>(endpoint: string, options: RequestOptions): Promise<T> {
const labels = { endpoint, method: options.method || 'POST' };
const endTimer = apolloRequestDuration.startTimer(labels);
try {
const response = await this.baseClient.request(endpoint, options);
apolloRequestsTotal.inc({ ...labels, status: 'success' });
// Track rate limit from headers
const remaining = response.headers['x-ratelimit-remaining'];
if (remaining) {
apolloRateLimitRemaining.set({ endpoint }, parseInt(remaining));
}
return response.data;
} catch (error: any) {
const errorType = this.classifyError(error);
apolloRequestsTotal.inc({ ...labels, status: 'error' });
apolloErrors.inc({ endpoint, error_type: errorType });
if (error.response?.status === 429) {
apolloRateLimitHits.inc();
}
throw error;
} finally {
endTimer();
}
}
private classifyError(error: any): string {
const status = error.response?.status;
if (status === 401) return 'auth_error';
if (status === 403) return 'permission_error';
if (status === 422) return 'validation_error';
if (status === 429) return 'rate_limit';
if (status >= 500) return 'server_error';
if (error.code === 'ECONNREFUSED') return 'connection_error';
if (error.code === 'ETIMEDOUT') return 'timeout';
return 'unknown';
}
}
// src/lib/apollo/logger.ts
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => ({ level: label }),
},
redact: {
paths: ['api_key', '*.email', '*.phone', 'headers.authorization'],
censor: '[REDACTED]',
},
base: {
service: 'apollo-integration',
environment: process.env.NODE_ENV,
},
});
export const apolloLogger = logger.child({ component: 'apollo' });
// Request/response logging
export function logApolloRequest(context: {
endpoint: string;
method: string;
params?: object;
requestId: string;
}): void {
apolloLogger.info({
type: 'apollo_request',
...context,
timestamp: new Date().toISOString(),
});
}
export function logApolloResponse(context: {
endpoint: string;
status: number;
durationMs: number;
requestId: string;
resultCount?: number;
}): void {
apolloLogger.info({
type: 'apollo_response',
...context,
timestamp: new Date().toISOString(),
});
}
export function logApolloError(context: {
endpoint: string;
error: Error;
requestId: string;
retryCount?: number;
}): void {
apolloLogger.error({
type: 'apollo_error',
endpoint: context.endpoint,
error: {
name: context.error.name,
message: context.error.message,
stack: context.error.stack,
},
requestId: context.requestId,
retryCount: context.retryCount,
timestamp: new Date().toISOString(),
});
}
// src/lib/apollo/tracing.ts
import { trace, Span, SpanStatusCode, context as otelContext } from '@opentelemetry/api';
import { W3CTraceContextPropagator } from '@opentelemetry/core';
const tracer = trace.getTracer('apollo-integration');
const propagator = new W3CTraceContextPropagator();
export function createApolloSpan(
name: string,
attributes: Record<string, any>
): Span {
return tracer.startSpan(`apollo.${name}`, {
attributes: {
'apollo.endpoint': attributes.endpoint,
'apollo.method': attributes.method,
'service.name': 'apollo-integration',
},
});
}
export async function traceApolloRequest<T>(
endpoint: string,
requestFn: () => Promise<T>
): Promise<T> {
const span = createApolloSpan('request', { endpoint });
try {
const result = await otelContext.with(
trace.setSpan(otelContext.active(), span),
requestFn
);
span.setStatus({ code: SpanStatusCode.OK });
return result;
} catch (error: any) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message,
});
span.recordException(error);
throw error;
} finally {
span.end();
}
}
// Middleware for Express
export function apolloTracingMiddleware(req: any, res: any, next: any) {
const span = createApolloSpan('http_request', {
endpoint: req.path,
method: req.method,
});
req.apolloSpan = span;
res.on('finish', () => {
span.setAttribute('http.status_code', res.statusCode);
span.end();
});
next();
}
# prometheus/apollo-alerts.yml