| name | apollo-observability |
| description | 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".
|
| allowed-tools | Read, Write, Edit, Bash(kubectl:*), Bash(curl:*) |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Apollo Observability
Overview
Comprehensive observability setup for Apollo.io integrations including metrics, logging, tracing, and alerting.
Metrics with Prometheus
import { Registry, Counter, Histogram, Gauge } from 'prom-client';
const register = new Registry();
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],
});
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],
});
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],
});
export const apolloCreditsUsed = new Counter({
name: 'apollo_credits_used_total',
help: 'Total Apollo credits consumed',
labelNames: ['operation'],
registers: [register],
});
export const apolloErrors = new Counter({
name: 'apollo_errors_total',
help: 'Total Apollo API errors',
labelNames: ['endpoint', 'error_type'],
registers: [register],
});
export { register };
Instrumented Client
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' });
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.({ endpoint, : errorType });
(error.?. === ) {
apolloRateLimitHits.();
}
error;
} {
();
}
}
(: ): {
status = error.?.;
(status === ) ;
(status === ) ;
(status === ) ;
(status === ) ;
(status >= ) ;
(error. === ) ;
(error. === ) ;
;
}
}
Structured Logging
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' });
export function logApolloRequest(context: {
endpoint: string;
method: string;
params?: object;
requestId: string;
}): void {
apolloLogger.info({
type: 'apollo_request',
...context,
timestamp: new Date().toISOString(),
});
}
(): {
apolloLogger.({
: ,
...context,
: ().(),
});
}
(): {
apolloLogger.({
: ,
: context.,
: {
: context..,
: context..,
: context..,
},
: context.,
: context.,
: ().(),
});
}
Distributed Tracing (OpenTelemetry)
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 });
{
result = otelContext.(
trace.(otelContext.(), span),
requestFn
);
span.({ : . });
result;
} (: ) {
span.({
: .,
: error.,
});
span.(error);
error;
} {
span.();
}
}
() {
span = (, {
: req.,
: req.,
});
req. = span;
res.(, {
span.(, res.);
span.();
});
();
}
Alerting Rules
groups:
- name: apollo-alerts
rules:
- alert: ApolloHighErrorRate
expr: |
sum(rate(apollo_errors_total[5m])) /
sum(rate(apollo_requests_total[5m])) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "High Apollo API error rate"
description: "Apollo error rate is {{ $value | humanizePercentage }}"
- alert: ApolloRateLimitApproaching
expr: apollo_rate_limit_remaining < 20
for: 2m
labels:
severity: warning
annotations:
summary: "Apollo rate limit approaching"
description: "Only {{ $value }} requests remaining"
- alert: ApolloRateLimitHit
expr: increase(apollo_rate_limit_hits_total[5m]) > 0
Grafana Dashboard
{
"dashboard": {
"title": "Apollo.io Integration",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "sum(rate(apollo_requests_total[5m])) by (endpoint)",
"legendFormat": "{{ endpoint }}"
}
]
},
{
"title": "Error Rate",
"type": "graph",
"targets": [
{
"expr": "sum(rate(apollo_errors_total[5m])) by (error_type)",
"legendFormat":
Health Check Endpoint
import { Router } from 'express';
import { register } from '../../lib/apollo/metrics';
const router = Router();
router.get('/health/apollo', async (req, res) => {
const checks = {
api: false,
rateLimit: false,
cache: false,
};
try {
await apollo.healthCheck();
checks.api = true;
const remaining = apolloRateLimitRemaining.get();
checks.rateLimit = remaining > 10;
const cacheStats = apolloCache.getStats();
checks.cache = cacheStats.size > 0;
const healthy = Object.values(checks).every(Boolean);
res.status(healthy ? 200 : 503).json({
status: healthy ? 'healthy' : 'degraded',
checks,
: ().(),
});
} (: ) {
res.().({
: ,
: error.,
checks,
});
}
});
router.(, (req, res) => {
res.(, register.);
res.( register.());
});
router;
Output
- Prometheus metrics for all Apollo operations
- Structured JSON logging with PII redaction
- OpenTelemetry distributed tracing
- Alerting rules for errors, rate limits, latency
- Grafana dashboard configuration
- Health check endpoints
Error Handling
| Issue | Resolution |
|---|
| Missing metrics | Verify instrumentation |
| Alert noise | Tune thresholds |
| Log volume | Adjust log levels |
| Trace gaps | Check propagation |
Resources
Next Steps
Proceed to apollo-incident-runbook for incident response.