| name | speak-observability |
| description | Set up comprehensive observability for Speak integrations with metrics, traces, and alerts.
Use when implementing monitoring for Speak operations, setting up dashboards,
or configuring alerting for language learning feature health.
Trigger with phrases like "speak monitoring", "speak metrics",
"speak observability", "monitor speak", "speak alerts", "speak tracing".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Speak Observability
Overview
Set up comprehensive observability for Speak language learning integrations.
Prerequisites
- Prometheus or compatible metrics backend
- OpenTelemetry SDK installed
- Grafana or similar dashboarding tool
- AlertManager configured
Key Metrics for Language Learning
Business Metrics
| Metric | Type | Description |
|---|
speak_lessons_started_total | Counter | Lessons initiated |
speak_lessons_completed_total | Counter | Lessons completed |
speak_lessons_abandoned_total | Counter | Lessons abandoned mid-session |
speak_pronunciation_score | Histogram | Pronunciation scores distribution |
speak_active_sessions | Gauge | Currently active lesson sessions |
speak_daily_active_learners | Gauge | Unique learners today |
Technical Metrics
| Metric | Type | Description |
|---|
speak_api_requests_total | Counter | Total API requests |
speak_api_duration_seconds | Histogram | Request latency |
speak_api_errors_total | Counter | Error count by type |
speak_speech_recognition_duration_seconds | Histogram | Audio processing time |
speak_rate_limit_remaining | Gauge | Rate limit headroom |
Prometheus Metrics Implementation
import { Registry, Counter, Histogram, Gauge } from 'prom-client';
const registry = new Registry();
const lessonsStarted = new Counter({
name: 'speak_lessons_started_total',
help: 'Total lessons started',
labelNames: ['language', 'topic', 'difficulty'],
registers: [registry],
});
const lessonsCompleted = new Counter({
name: 'speak_lessons_completed_total',
help: 'Total lessons completed',
labelNames: ['language', 'topic', 'difficulty'],
registers: [registry],
});
const pronunciationScore = new Histogram({
name: 'speak_pronunciation_score',
help: 'Pronunciation scores distribution',
labelNames: ['language'],
buckets: [40, 50, 60, 70, 80, 85, 90, , ],
: [registry],
});
activeSessions = ({
: ,
: ,
: [],
: [registry],
});
apiRequests = ({
: ,
: ,
: [, , ],
: [registry],
});
apiDuration = ({
: ,
: ,
: [, ],
: [, , , , , , , ],
: [registry],
});
speechRecognitionDuration = ({
: ,
: ,
: [],
: [, , , , , ],
: [registry],
});
Instrumented Speak Client
async function instrumentedSpeakRequest<T>(
method: string,
endpoint: string,
operation: () => Promise<T>
): Promise<T> {
const timer = apiDuration.startTimer({ method, endpoint });
try {
const result = await operation();
apiRequests.inc({ method, endpoint, status: 'success' });
return result;
} catch (error: any) {
apiRequests.inc({ method, endpoint, status: 'error' });
throw error;
} finally {
timer();
}
}
async function trackLessonStart(session: LessonSession): Promise<void> {
lessonsStarted.inc({
language: session.language,
topic: session.topic,
difficulty: session.difficulty,
});
activeSessions.inc({ language: session.language });
}
async function (): <> {
labels = {
: session.,
: session.,
: session.,
};
(summary.) {
lessonsCompleted.(labels);
} {
lessonsAbandoned.({
...labels,
: summary. || ,
});
}
activeSessions.({ : session. });
(summary.) {
pronunciationScore.(
{ : session. },
summary.
);
}
}
Distributed Tracing
OpenTelemetry Setup
import { trace, SpanStatusCode, context, propagation } from '@opentelemetry/api';
const tracer = trace.getTracer('speak-service');
async function tracedSpeakCall<T>(
operationName: string,
attributes: Record<string, string>,
operation: () => Promise<T>
): Promise<T> {
return tracer.startActiveSpan(`speak.${operationName}`, async (span) => {
span.setAttributes(attributes);
try {
const result = await operation();
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();
}
});
}
async (): <> {
(
,
{
: userId,
: config.,
: config.,
},
() => {
session = speakService.(config);
(!session.) {
(
,
{ : session. },
() => {
prompt = session.();
response = ();
session.(response);
}
);
}
session.();
}
);
}
Structured Logging
import pino from 'pino';
const logger = pino({
name: 'speak-service',
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => ({ level: label }),
},
});
interface SpeakLogContext {
service: 'speak';
operation: string;
sessionId?: string;
userId?: string;
language?: string;
duration_ms?: number;
pronunciationScore?: number;
error?: any;
}
function logSpeakOperation(
level: 'info' | 'warn' | 'error',
operation: string,
context: Partial<SpeakLogContext>
): void {
const logContext: SpeakLogContext = {
service: 'speak',
operation,
...context,
};
logger[level](logContext, `Speak `);
}
(, , {
: session.,
: session.,
: session.,
: summary.,
: summary.,
});
Alert Configuration
Prometheus AlertManager Rules
groups:
- name: speak_alerts
rules:
- alert: SpeakHighErrorRate
expr: |
rate(speak_api_errors_total[5m]) /
rate(speak_api_requests_total[5m]) > 0.05
for: 5m
labels:
severity: warning
service: speak
annotations:
summary: "Speak API error rate > 5%"
description: "Error rate is {{ $value | humanizePercentage }}"
- alert: SpeakSpeechRecognitionSlow
expr: |
histogram_quantile(0.95,
rate(speak_speech_recognition_duration_seconds_bucket[5m])
) > 5
for: 5m
labels:
severity: warning
service: speak
annotations:
summary: "Speech recognition P95 latency > 5s"
- alert: SpeakLowCompletionRate
expr: |
rate(speak_lessons_completed_total[1h]) /
rate(speak_lessons_started_total[1h]) < 0.5
Grafana Dashboard
{
"title": "Speak Language Learning",
"panels": [
{
"title": "Active Lesson Sessions",
"type": "stat",
"targets": [{
"expr": "sum(speak_active_sessions)"
}]
},
{
"title": "Lessons Started vs Completed",
"type": "graph",
"targets": [
{ "expr": "rate(speak_lessons_started_total[5m])", "legendFormat": "Started" },
{ "expr": "rate(speak_lessons_completed_total[5m])", "legendFormat"
Output
- Business and technical metrics
- Distributed tracing configured
- Structured logging implemented
- Alert rules deployed
- Grafana dashboard ready
Error Handling
| Issue | Cause | Solution |
|---|
| Missing metrics | No instrumentation | Wrap client calls |
| Trace gaps | Missing propagation | Check context headers |
| Alert storms | Wrong thresholds | Tune alert rules |
| High cardinality | Too many labels | Reduce label values |
Examples
Quick Metrics Endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', registry.contentType);
res.send(await registry.metrics());
});
Resources
Next Steps
For incident response, see speak-incident-runbook.